ulCriticalnesting not on initial task stack?

wz2b wrote on Saturday, May 26, 2007:

I am trying to sort out why my LPC2129 / GCC system is not working.  My port began life as GCC/ARM7_LPC2000 modified to match my hardware and address map.  In port.c I find a note that says:

    Changes from V2.5.2
    + ulCriticalNesting is now saved as part of the task context, as is
      therefore added to the initial task stack during pxPortInitialiseStack.

However, examining pxPortInitialiseStack() I do not see this happening.  In fact, there is no other reference to ulCriticalNesting anywhere in that file.

Second, I see that ulCriticalNesting defaults to 9999.  In a different port I found a note that says that this is to ensure that FreeRTOS knows that interrupts are off during initialization.  It indicates that this will be set to 0 once the first task is started.  I do not see where this happens…it is not in any of these:


I THINK that this should be in vPortISRStartFirstTask() prior to calling the portRESTORE_CONTEXT() macro.

Can anybody shed some light on what is going on here?


rtel wrote on Sunday, May 27, 2007:

The critical section nesting depth is zero when the task first starts.  It is the last thing placed onto the initial task stack in pxPortInitialiseStack() with the following line:

/* Some optimisation levels use the stack differently to others.  This
means the interrupt flags cannot always be stored on the stack and will
instead be stored in a variable, which is then saved as part of the
tasks context. */

The value is then popped off the stack and placed into the ulCriticalNesting within the macro portRESTORE_CONTEXT() with the following lines:

/* The critical nesting depth is the first item on the stack. */   
/* Load it into the ulCriticalNesting variable. */       
"LDR        R0, =ulCriticalNesting                \n\t"   
"LDMFD    LR!, {R1}                        \n\t"   
"STR        R1, [R0]                    \n\t"    \


wz2b wrote on Sunday, May 27, 2007:

Thanks.  I missed that.  In light of this, there must be something else wrong with the initial stack, such that when it attempts to context switch to the first task it has an invalid LR and aborts.  I will trace further.

I was reflecting on this problem last night, and it got me wondering something more generic.  My understanding of the purpose of keeping a critical nesting depth does not mesh with the idea of having to preserve that nesting depth across context switches.  In other words, if you were to enter a critical section, then there should not even be a context switch until you have exited that critical section.  Within that context, you may want to call functions that also have critical sections; the nesting depth prevents interrupts from being turned back on until you have exited the last of those, i.e. the outer-most; nesting depth goes to 0 and you are no longer in any critical section.

Why, then, should the nesting depth be part of the task state, rather than a system-wide global?


davedoors wrote on Sunday, May 27, 2007:

99 times out of 100 this is because you are not in Supervisor mode when you attempt to start the scheduler.  It is easiest to call main() in Supervisor mode.  Use the startup files provided with the port you based yours on.

While a preemptive context switch will not occur from within a critical section, you still have the option to perform a switch if you want to.  If you do this you can be assured that interrupts are disabled when the task starts running again (but will not necessarily be disabled while other tasks are running).  This makes for a very flexible system, more so that (most?) other systems?


wz2b wrote on Sunday, May 27, 2007:

Yep look at that, I was ending in SYSTEM mode, not SUPERVISOR mode.  Ha!

I make it all the way to the idle task now.  For some reason when the idle task yields it puts me into a bad place (instead of starting my first real task).

davedoors wrote on Sunday, May 27, 2007:

Are you -

Setting up stacks of Supervisor and IRQ modes?  There is no need to setup stacks for User mode as this is done when the task is created.

Vectoring directly to your SWI and IRQ handlers without going through some intermediary code?


wz2b wrote on Sunday, May 27, 2007:

Good point.  Yes I am setting up USR – I will fix that next.  I also set up an abort stack that I probably also don’t need, since abort does nothing but loop to itself.  (Maybe later I will want abort to do something more useful).

I noticed some questions about initialization elsewhere on the forums.  I will post my startup for the LPC2129/GCC in case it is useful for others.  There are two snippets attached: my startup.s as well as lpc2129.x (linker script).  I tried to make the linker configuration reasonably minimal.  It allows for placement and location of initialized data at separate locations (ram and flash) and all of that seems to work well for me so far.

— cut here —
#define RAM_BASE 0x40000000
#define RAM_SIZE 16*1024

#define USR_STACK_SIZE  2048

#define Mode_USR    0x10
#define Mode_FIQ    0x11
#define Mode_IRQ    0x12
#define Mode_SVC    0x13
#define Mode_ABT    0x17
#define Mode_UNDEF  0x1B
#define Mode_SYS    0x1F

#define I_Bit      0x80
#define F_Bit      0x40

        .global _start
        .global undef_routine
        .global data_abort
        .global prefetch_abort
        .global startup_routine

        // set the SYS, I, and F bits in the CONTROL field of the CPU status register
        // I is the interrupt mask
        // F is the fast irq mask
        // T indicates if we are in thumb mode … it is bit 0x20
        // The lower 5 bits are the MODE bits.  Mode 0x1F is System mode.
        msr cpsr_c, #Mode_SYS | I_Bit | F_Bit

        // Load the static pointer to top of ram

        // Call the low level initialization
        bl lowlevelinit

        // switch to irq mode and set his stack pointer too
        msr cpsr_c, #Mode_IRQ | F_Bit | I_Bit

        // switch to undefined mode and set his stack pointer
        msr cpsr_c, #Mode_UNDEF | F_Bit | I_Bit

        // switch to fast irq mode and set his stack pointer
        msr cpsr_c, #Mode_FIQ | F_Bit | I_Bit

        // switch to abort mode and set his stack pointer
        msr cpsr_c, #Mode_ABT | F_Bit | I_Bit

        // switch to supervisor  mode and set his stack pointer
        msr cpsr_c, #Mode_SVC | F_Bit | I_Bit

        // Switch back to supervisor
        msr cpsr_c, #Mode_SVC

        bl              clear_bss
        bl              initialize_sdata

        mov             a2, #0                  /* Fill value */
        mov             fp, a2                  /* Null frame pointer */
        mov             r7, a2                  /* Null frame pointer for Thumb */

        mov             r0, #0          /* no arguments  */
        mov             r1, #0          /* no argv either */

        b main

// Clear BSS

        ldr     r1, =__bss_start__
        ldr     r3, =__bss_end__
        subs    r3, r3, r1
        beq     .end_clear_loop

        mov     r2, #0
        strb    r2, [r1], #1
        subs    r3, r3, #1
        bgt     .clear_loop

        mov     pc, lr

// Initialize data section


        ldr             r1, =_etext
        ldr             r2, =__data_start__
        ldr             r3, =__data_end__

        cmp r2, r3
        ldrlo r0, [r1], #4
        strlo r0, [r2], #4
        blo loopdat

        // done
        mov     pc, lr
— end —

Now, in order to use this you have to have the corresponding linker configuration file, that defines the correct symbols for various pieces of memory.  Here’s what I came up with:

— cut here —
/* Default linker script, for normal executables */

   * Code space is 256KB minus 8KB for the bootloader area
   * that cannot be used
  text   (rx)           : ORIGIN = 0x00000000,      LENGTH = 0x3E000

   * RealMonitor (if present) uses 0x40000040 - 0x4000011F
   * In System Programming uses 0x40000120 - 0x400001FF
   * Flash programming commands use the top 32 bytes of on-chip RAM.
   * The resulting useful ram is 0x40000200 through 0x40003FE0
  data   (rwx)          : ORIGIN = 0x40000200,      LENGTH = 0x3DE0

/* OUTPUT_FORMAT("elf32-littlearm") */


  .text           :
    _start_of_text_ = .;
    *(.text .stub .text.*)
    *(.glue_7t) *(.glue_7)
  } > text

  _etext = .;

  /* RAM */

  .data : AT ( ADDR(.text) + SIZEOF(.text) )
    __data_start__ = . ;
    . = ALIGN(2);
    *(.data .data.* .gnu.linkonce.d.*)
    . = ALIGN(2);
     __data_end__ = . ;
  } > data

  .data1          : { *(.data1) }
  .tdata          : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
  .tbss           : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
  .eh_frame       : { KEEP (*(.eh_frame)) }
  .gcc_except_table   : { *(.gcc_except_table) }
  .dynamic        : { *(.dynamic) }

  _edata = .;
  PROVIDE (edata = .);

  /* Uninitialized data */
  __bss_start = .;
  __bss_start__ = .;
  .bss            :
   *(.bss .bss.* .gnu.linkonce.b.*)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.  */
   . = ALIGN(32 / 8);
  . = ALIGN(32 / 8);
  _end = .;
  _bss_end__ = . ; __bss_end__ = . ; __end__ = . ;

  PROVIDE (end = .);
  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }

  .comment       0 : { *(.comment) }

  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */

  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }

  .stack 0x40003FDC :
    _stack = .;

  .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
  /DISCARD/ : { *(.note.GNU-stack) }
  PROVIDE (__stack__ = 0xa00) ;

— end —