This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

RTOS/EK-TM4C1294XL: Empty idle task stack overflow

Part Number: EK-TM4C1294XL
Other Parts Discussed in Thread: SYSBIOS

Tool/software: TI-RTOS

Hi,

my TI RTOS is up and running with some tasks. Now I want to add a new one and suddenly the idle task throws a stackoverflow exception. I cannot understand how this happens because my idle task is empty (no idle functions configured). When I comment out the construction of the new task everything is fine.

Here some Details:

Error console:

l.Task: line 383: E_spOutOfBounds: Task 0x20015df8 stack error, SP = 0x20012924.
xdc.runtime.Error.raise: terminating execution

ROV Tasks:

Can anyone identify the problem?

Best regards

  • Hi,

    It looks like there is some major corruption going on. The two tasks objects (0x200158a8 and 0x200158f8) appear to be over-written by something. What is at the memory address before them. Are you creating the tasks via Task_create or Task_construct (or in the .cfg file)? If you use Task_create, the object is coming out of the heap, so it's probably hard to figure out what is before it. You can try using HeapTrack. It places a scribble at the end of the allocated block and helps you catch over-writing issues.

    This is a good resource on how to use HeapTrack (and a couple other debugging techniques): training.ti.com/debugging-common-application-issues-ti-rtos

    Todd
  • Hi,

    I use Task_construct for the tasks. All of the tasks are constructed from within different c files and I have the variables (stack, handle etc) declared at the respective file scope. Is it possible that this is my problem? Where do I safely declare task stack and handle in order to not have it overwritten by other operations?

    Best regards
  • Are the Task_Struct's and stacks global or local variables?

    You should not have a Task_Struct be a local variable. Task_construct uses that memory you provide in the Task_Struct to maintain state, so it must be persistent for the life of the Task. When you have a Task_Struct as a local variable, it is placed on the stack. If you leave the function that has the local Task_Struct, and then call some other function, the Task_Struct will be corrupted. You have the following choices
    1. Use Task_create. It allocates the Task object from memory.
    2. Use Task_construct, but make sure the Task_Struct is persistent (e.g. global variable or allocate the memory...but for this case, just use Task_create).
    3. Create the task statically in the .cfg file.

    Similar logic for the stacks.

    Here is a more detailed look at the pros and cons of each approach: processors.wiki.ti.com/.../TI-RTOS_Object_Creation_Comparison

    Also, are you providing Task_construct with the address of a Task_Struct or a Task_Object?

    Todd
  • Hi ToddMullanix,

    the task structs and stacks are declared globally in different c files (in the files of the respective task). Changing from Task_construct to Task_create did not solve the problem, only the stack addresses that are now given in the thrown exception changed.

    Here is an example of the task creation:

      

    Task_Params adc_sample_taskParams;
    #define TASKSTACKSIZE_ADC_SAMPLE   2048
    Task_Struct task_adc_sample_struct;
    //Char task_adc_sample_stack[TASKSTACKSIZE_ADC_SAMPLE];
    Task_Handle handle_task_adc_sample;
    Error_Block task_adc_eb;
    
    function createTask(){
        Error_init(&task_adc_eb);
        Task_Params_init(&adc_sample_taskParams);
    
        adc_sample_taskParams.arg0 = 1000;
        adc_sample_taskParams.stackSize = TASKSTACKSIZE_ADC_SAMPLE;
        //adc_sample_taskParams.stack = &task_adc_sample_stack;
        //Task_construct(&task_adc_sample_struct, (Task_FuncPtr)task_adc_add_sample, &adc_sample_taskParams, NULL);
        Task_create((Task_FuncPtr)task_adc_add_sample, &adc_sample_taskParams, &task_adc_eb);
        //handle_task_adc_sample = Task_handle(&task_adc_sample_struct);
    }

    Best regards

  • The code looks fine. Can you set a breakpoint after each Task_create and confirm that nothing looks bad in ROV->Task->Detailed?

    E_spOutOfBounds means that when the kernel is switching to another Task, the SP for that task is not within the tasks stack. This is generally caused by corruption. Can you reproduce the error with the Task_creates (instead of Task_constructs) and let the stack be allocated (basically the above code). Then attach a snapshot of the error and ROV (like you did in the initial post).
  • Hi,

    with Task_create instead of Task_construct the problem remains the same. Same result also if I declare the Task via the .cfg file. I disabled some of the other tasks to isolate the problem a little bit. The said task is created before bios_init() btw.

    I made 3 ROV screenshots

    Before task creation:

    Line after task creation:

    Error state:

    Also here is my Task module configuration:

    Best regards

  • Something is wrong even before your first ROV picture. That last task at 0x20014458 looks bad. Where was it created? Can you include the C or .cfg code that created it? If it is in the .cfg, can you just attached the .cfg (please don't copy/paste the file into the thread....the thread gets long and hard to read). The C code you added above is for the task_adc_add_sample task that shows up in the second pic (and it looks fine).

    Can you look in the mapfile and see what is at memory address 0x20014458. Is this a Task_Struct that is not constructed? In newer versions of the kernel, we show "not constructed" in ROV.
  • Thing is the task without label appears from out of nowhere. I created a minimal example that shows the problem. What it basically does is it samples 8 ADC inputs and stores the value in "pui32ADC0Value". This happens every 1ms. The configuration of ADC and DMA are in the inc/minimal_ADC.c file.

    I just found out that the problem only occurs when the HWI hwi_adc0() (minimal_ADC.c line 135) posts a Semaphore for the adc result computation task. This happens in line 147. If I comment out the Semaphore everything works.

    How can this be?

    Attached you can find the minimal example.

    Best regards!

    empty_EK_TM4C1294XL_TI.zip

  • First of all, thanks for the stripped down project. It makes things so much easier to debug!

    I see one problem. The following is in .cfg: 

    var hwi1Params = new Hwi.Params();
    hwi1Params.instance.name = "handle_hwi_adc0";
    hwi1Params.priority = 12;
    hwi1Params.maskSetting = xdc.module("ti.sysbios.interfaces.IHwi").MaskingOption_LOWER;
    Program.global.handle_hwi_adc0 = Hwi.create(30, "&hwi_adc0", hwi1Params);

    This is telling the kernel to not manage this interrupt. We call this a zero-latency interrupt in the kernel since the kernel adds no latency. These have their place in the applications. For example, you want to make sure the kernel never disables it or you need the absolute smallest latency. However you cannot make any kernel calls that may impact scheduling in a zero-latency interrupt...for example the Semaphore_post you are calling.

    What makes it a zero-latency interrupt is the priority being less than Hwi.disablePriority (which is 32 for TM4C). Take a look at the green circled answer on this thread for more details: 

    Note: you can see that it zero-latency here also in ROV

    Do you really need a zero-latency interrupt? If not, either leave the priority blank (and take the default) or set it to a number above 32.

    The weirdness is a result of messing up the scheduler by calling Semaphore_post in the zero-latency interrupt.

    Todd

  • I feel validated that I am not the only person to fall for this.

    Is there a comprehensive document that describes TI-RTOS?

    Edit: to clarify, I typically reference the cdoc html and kernel user guide pdf (SPRUEX3T). The wiki seems like random fun facts. I rarely consider the wiki. What source is complete?

  • Awesome, that resolved my issue! Thank you very much for your effort!

    So priorities over 32 can be used? What is the upper limit?

    Best regards
  • 32 is the limit for SWI priorities on TM4C. I don't see a limit for the HWIs, but the struct uses an int for this parameter, so you can't exceed the max int value.
  • There are 256 priorities for the interrupts. Take a look at the cdoc for the ti.sysbios.family.arm.m3.Hwi module (e.g. software-dl.ti.com/.../Hwi.html) for more details.

    Todd