C++ member variables are dereferenced upon task entry

Hello FreeRTOS ARM forum…first time poster and new FreeRTOS user.

We are developing on the cortex M7 using IAR Embedded Workbench v9.2. I have successfully implemented a FreeRTOS application in “C” without much difficulty. My team ultimately wants to develop in C++ as we have a large legacy code base in this language.

Not being all that great a C++ programmer, I read some things on this forum to attempt to implement a C++ wrapper for running tasks in a c++ context. In particular, I implemented a subset of Richard-Damon’s wrapper examples.

At first, everything seemed to work. I launched a couple simple tasks that printed out to the UART console once/second.

I then attempted a more “complicated” example that read data from a SPI device and printed this to the console once/second. This example would hard-fault and ultimately the root cause is that the C++ class variables are being dereferenced when the C++ task function is called.

My C++ class looks like…

class clsTTCIMUTask: public clsTTCTaskBase{
public:
  void vCreateTask(void);
  void vReadEWTS5GValueCmd(uint8_t sensor);
  uint8_t CRC_calculation(uint8_t data1, uint8_t data2, uint8_t data3, uint8_t data4);
  
  void imu_task(void);
  static void taskfun(void* parm){
    static_cast<clsTTCIMUTask *>(parm)->imu_task();
  }

protected:
  const char * pcMyTaskName = "IMU_TASK";
  uint32_t masterRxData;
  uint32_t masterTxData;
  ecspi_master_config_t masterConfig;
  ecspi_transfer_t masterXfer;
  ecspi_master_handle_t g_m_handle;  
};

I instantiate my class at filescope in my main.cpp file as follows…

clsTTCIMUTask imu_ecspi1_task;

int main()
{
    BOARD_InitMemory();

    /* Board specific RDC settings */
    BOARD_RdcInit();

    BOARD_InitBootPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();

    copyResourceTable();
    
    CLOCK_SetRootMux(kCLOCK_RootEcspi1, kCLOCK_EcspiRootmuxSysPll1); /* Set ECSPI1 source to SYSTEM PLL1 800MHZ */
    CLOCK_SetRootDivider(kCLOCK_RootEcspi1, 2U, 5U);                 /* Set root clock to 800MHZ / 10 = 80MHZ */

    /* Set IRQ priority for freertos_ecspi */
    NVIC_SetPriority(EXAMPLE_ECSPI_MASTER_IRQN, 2);

    imu_ecspi1_task.vCreateTask();
    
    vTaskStartScheduler();

    (void)PRINTF("Failed to start FreeRTOS on core0.\r\n");
    for (;;)
    {
    }  
  return 0;
}

The vCreateTask() function is as follows…

void clsTTCIMUTask::vCreateTask(void){
    taskStackSize = 256;
    taskPriority = 2;
    
    if (xTaskCreate(&taskfun, pcMyTaskName, taskStackSize, NULL, taskPriority, &taskHandle) != pdPASS)
    {
        (void)PRINTF("\r\nFailed to create IMU task\r\n");
        for (;;)
        {
        }
    }  
}

When the scheduler starts my C++ function, I can see with the debugger that the C++ member functions of the class (such as “masterRxData”) have been dereferenced and now point to locations within the Vector table! When data is put into the relocated masterRxData, the machine hard faults because masterRxData now points to the vector table location for a Bus Fault…

I was able to kluge the code to work by doing an extern of the instantiated class in the .cpp for the class itself and explicitly referring to the instantiated class…eg
imu_ecspi1_task.masterRxData…

How have others addressed this issue?
Thanks for your patience.
RichM

Did you define configASSERT , enable stack overflow checking for development/debugging.
Your issue sounds weird and could be related to stack/data corruption.
Assuming your linker script and startup code are ok. Did you track that the constructor is properly executed and the instance is correctly initialized ?
Try putting a breakpoint there and see what’s going on and keep an eye on this piece of memory (dump) or watch the object until/when the task function is executed (where I’d put the next breakpoint).

hs2 - configASSERT is defined but I have not implemented stack overflow checking. As illustrated, I don’t currently have a constructor but I will add one because I agree it would help. I can see the imu_ecspi1_task object in memory before I call the vCreateTask function. I single stepped into the freertos xTaskCreate function and verified the parameters such as stack size, priority etc are getting set correctly. The problems start when the scheduler is started…I have set my linker heap size to the same size as the FreeRTOSConfig.h file (approx 40kbytes). My C++ stack size is 1Kb…Will advise when I see results from the addition of the constructor…

You have code that writes data to a console. Are you sure that those routines are re-entrant? Are they protected with a mutex?
Before the scheduler starts it won’t need a mutex of course.

    clsTTCIMUTask imu_ecspi1_task;

Hartmut wrote:

Assuming your linker script and startup code are ok. Did you track that the constructor is properly executed and the instance is correctly initialized ?

imu_ecspi1_task is declared as a global variable, and just like in C the object should be cleared at boot-time.
If you add an explicit constructor, can you check if it really gets called, by placing a break-point, or by setting some variable? And if you don’t have a constructor, make sure that the data is cleared at boot-time.

Also check if this assignment works as it should:

protected:
	const char * pcMyTaskName = "IMU_TASK";

You can check that easily from a debugger.

Mind you that in embedded projects often C-only start-up code is included as an assembler file. I once wrote a C++ start-up routine in C/assembler. It would inspect the arrays of constructor calls, set the the .data section, and clear the .bss section.

I recommend to be careful with the use of constructors in case they get called at boot-time. These constructors are called before main() is active. The heap for instance, might not be initialised yet. The stack will be the initial main stack, which may not be sufficient for all actions. And of course, the scheduler is not running yet.

For automatic C++ variables (declared on the stack), there is no problem. Their constructors are called when the function is running.

And finally I would avoid static variables declared within a function. I would place them outside the function as a static variable.

My C++ stack size is 1Kb

You know better the we how much stack is used in you function. 1 KB normally means 1024 words of stack.

EDIT : Are you sure that imu_ecspi1_task is not declared on the stack of main()?

could it be that your class variables are statically created automatic variables on the startup stack? If so, they are being overwritten by ISRs (FreeRTOS fallacy #4).

Search for “main() stack” on this page: https://www.freertos.org/FAQHelp.html

Thanks for the responses…

To clarify, I am NOT declaring my class within the main function. It is declared at file-scope within the main.cpp file. I can clearly see that this object is allocated within the .bss section of memory.

I added a constructor to the class that does nothing more than initialize a class pointer to itself. This is getting called and initialized correctly.

The first c++ function that is called from main is the ims_ecspi1_task.vCreateTask() function. As I step thru this function I can see the stack size and priority and name are being set at the correct location in .bss memory. One question I had was how the c++ “this” context is retrieved when a c++ function is entered. When stepping into the vCreateTask function I can see that the object “this” address is maintained in registers R0 and R4.

Now I let the scheduler start and enter my c++ task function. Registers R0 and R4 are zero and the class object reference locations reflect this as well…As the member variables are changed in this function, the object in .bss memory is unchanged because the object has been dereferenced.

I have tried to increase both the c++ stack size as well as the task stack size with no change in results.

I have been reading up on static c++ member functions and found the following quote which I have no idea if it is true or not but…“A static member function differs from a regular member function in that it can be called without an instance of a class, and since it has no instance, it cannot access non-static members of the class.” Seems like this is describing my exact problem…

Thanks for listening…
Rich

Oh yes - you should pass the object (this) pointer to the create function. I missed this bug in your original post :frowning:

Otherwise indeed you dereference a nullptr instead of the real object / this pointer.

Yup…that was it…seems like this should be in a FAQ somewhere…Anyway, thanks for your help!
Rich