PIC32 Soft Reset to Application Start

Hello, I am running FreeRTOS on a PIC32. My design has a bootloader and due to an error in the bootloader for units already in the field, I have the need to reset the micro to the start of the application. Basically I enter a sleep mode and need to shutdown a ton of stuff in a task before I do. When i wake up I would just reset the processor to restart everything from fresh. Unforutnately the bootloader bug prevents me from doing this. So what I have done is suspended the sheduler and performed a Jump to the start address of my application code. I am doing the jump to the start of the C startup routine not just the main function. I am seeing that the code correctly executes the jump, runs the code before my main app and then starts running my main app where I initialize the RTOS and its tasks. When I start the scheduler it remembers where I was in my sleep task and loads it back up to the spot where i jump to my application startup. So I reset over and over again. I am using C32 V1.12  I am wondering if anyone else has had experience with FreeRTOS remembering things. Its also possible that C32 isn’t performing effective clear on the startup. Does anyone have a better suggesting for reinitializing everything so that the RTOS doesn’t remember its self? Thanks for your help!
Jason

PIC32 Soft Reset to Application Start

** added to monitor post

PIC32 Soft Reset to Application Start

If I understand your post correctly, then you are jumping to the start, running the C start up code which should zero out variables and initialise variables as necessary, then running main() again to create your tasks – but you are then finding that tasks that were running before the reset start running again from where they left off. Do you then have two copies of the task – or is it that a task that was recreated executes from where it left off?  It is very hard to see how that could happen if everything was cleared to zero.  Do you have any special linker sections that the standard C start up code is not clearing? Which heap implementation are you using (heap_1, heap_2, heap_3 or heap_4, or your own)? Are you also disabling the tick interrupt before you restart? Can you experiment by having file scope static and global variables, and function scope static variables,  set to both zero and non-zero values and ensure they get reset to their initial state when you restart your program. Regards.

PIC32 Soft Reset to Application Start

You understand perfectly! I have one copy of the task. When the main reinitializes everything and starts the scheduler, that task resumes from where it left off which is right before I do my jump to reinitialize everything. Using Heap_3
I disable the tick interrupt (all interrupts for that matter) before i restart. I am not 100% sure that the linker is performing a C flush. I will have to experiment with that to be sure what I think is happening is actually happening. Below is the linker script. I am leaving a section of calibration request data present so my bootloader can pick it up, but that should be it.
OUTPUT_FORMAT("elf32-tradlittlemips")
OUTPUT_ARCH(pic32mx)
ENTRY(_reset)
EXTERN (_min_stack_size _min_heap_size)
PROVIDE(_min_stack_size = 0x400) ;
PROVIDE(_min_heap_size = 0) ;
INPUT("processor.o")
PROVIDE(_vector_spacing = 0x00000001);
_ebase_address  = 0x9D00C000;
_ORIGINAL_RESET_ADDR     = 0xBFC00000;
_RESET_ADDR              = 0x9D00B000;
_BEV_EXCPT_ADDR          = 0x9D00B380;
_DBG_EXCPT_ADDR          = 0x9D00B480;
_DBG_CODE_ADDR           = 0xBFC02000;
_GEN_EXCPT_ADDR          = _ebase_address + 0x180;
MEMORY
{
  startup_without_bootloader : ORIGIN = 0xBFC00000, LENGTH = 0x8
  kseg0_program_mem    (rx)  : ORIGIN = 0x9D00D000, LENGTH = 0x72000
  kseg0_boot_mem             : ORIGIN = 0x9D00B490, LENGTH = 0x970
  exception_mem              : ORIGIN = 0x9D00C000, LENGTH = 0x1000
  kseg1_boot_mem             : ORIGIN = 0x9D00B000, LENGTH = 0x490
  debug_exec_mem             : ORIGIN = 0xBFC02000, LENGTH = 0xFF0
  upgradeword                : ORIGIN = 0x9D07FFF8, LENGTH = 0x04
  checkword                  : ORIGIN = 0x9D07FFFC, LENGTH = 0x04
  config3                    : ORIGIN = 0xBFC02FF0, LENGTH = 0x4
  config2                    : ORIGIN = 0xBFC02FF4, LENGTH = 0x4
  config1                    : ORIGIN = 0xBFC02FF8, LENGTH = 0x4
  config0                    : ORIGIN = 0xBFC02FFC, LENGTH = 0x4
  kseg1_data_mem       (w!x) : ORIGIN = 0xA0000000, LENGTH = 0x18000
  reset_safe_area      (w!x) : ORIGIN = 0xA0018000, LENGTH = 0x7FF0
  calibration_request  (w!x) : ORIGIN = 0xA001FFF0, LENGTH = 0x10
  sfrs                       : ORIGIN = 0xBF800000, LENGTH = 0x100000
  configsfrs                 : ORIGIN = 0xBFC02FF0, LENGTH = 0x10  
}
SECTIONS
{
  .config_BFC02FF0 : {
    KEEP(*(.config_BFC02FF0))
  } > config3
  .config_BFC02FF4 : {
    KEEP(*(.config_BFC02FF4))
  } > config2
  .config_BFC02FF8 : {
    KEEP(*(.config_BFC02FF8))
  } > config1
  .config_BFC02FFC : {
    KEEP(*(.config_BFC02FFC))
  } > config0
}
SECTIONS
{
  .upgradeword :
  {
      LONG(0x0091A000);
  } >upgradeword
  .checkword :
  {
      LONG(0x02B3C000);
  } >checkword
  .startup_without_bootloader _ORIGINAL_RESET_ADDR :
  {
      /*  creates a “j _reset” instruction at address _ORIGINAL_RESET_ADDR 
            _reset must be within the same 256MB boundary as the original reset
            vector in order for this to work do to the limitations of the ‘j’ instruction.
      */
      LONG( ((_reset >> 2) & 0x03FFFFFF) | 0x08000000);
      LONG(0);
  } > startup_without_bootloader
  /* Boot Sections */
  .reset _RESET_ADDR :
  {
    KEEP(*(.reset))
    /*KEEP(*(.reset.startup))*/
  } > kseg1_boot_mem
  .bev_excpt _BEV_EXCPT_ADDR :
  {
    KEEP(*(.bev_handler))
  } > kseg1_boot_mem
  .dbg_excpt _DBG_EXCPT_ADDR (NOLOAD) :
  {
    . += (DEFINED (_DEBUGGER) ? 0x8 : 0x0);
  } > kseg1_boot_mem
  .dbg_code _DBG_CODE_ADDR (NOLOAD) :
  {
    . += (DEFINED (_DEBUGGER) ? 0xFF0 : 0x0);
  } > debug_exec_mem
  .app_excpt _GEN_EXCPT_ADDR :
  {
    KEEP(*(.gen_handler))
  } > exception_mem
  .vector_0 _ebase_address + 0x200 :
  {
    KEEP(*(.vector_0))
  } > exception_mem
  ASSERT (_vector_spacing == 0 || SIZEOF(.vector_0) <= (_vector_spacing << 5), "function at exception vector 0 too large")
  .vector_1 _ebase_address + 0x200 + (_vector_spacing << 5) * 1 :
  {
    KEEP(*(.vector_1))
  } > exception_mem
  ASSERT (_vector_spacing == 0 || SIZEOF(.vector_1) <= (_vector_spacing << 5), "function at exception vector 1 too large")
  .vector_2 _ebase_address + 0x200 + (_vector_spacing << 5) * 2 :
  {
    KEEP(*(.vector_2))
  } > exception_mem
cut the rest of the vectors off
  .startup ORIGIN(kseg0_boot_mem) :
  {
    KEEP(*(.startup))
  } > kseg0_boot_mem
  /* Code Sections */
  .text ORIGIN(kseg0_program_mem)  :
  {
    _text_begin = . ;
    *(.text .stub .text.* .gnu.linkonce.t.*)
    KEEP (*(.text.*personality*))
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
    *(.mips16.fn.*)
    *(.mips16.call.*)
    _text_end = . ;
  } >kseg0_program_mem =0
  /* Read-only sections */
  .rodata   :
  {
    *(.rodata .rodata.* .gnu.linkonce.r.*)
    *(.rodata1)
    . = ALIGN(4) ;
  } >kseg0_program_mem
  /*
   * Small initialized constant global and static data can be placed in the
   * .sdata2 section.  This is different from .sdata, which contains small
   * initialized non-constant global and static data.
   */
  .sdata2 ALIGN(4)   :
  {
    *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
    . = ALIGN(4) ;
  } >kseg0_program_mem
  /*
   * Uninitialized constant global and static data (i.e., variables which will
   * always be zero).  Again, this is different from .sbss, which contains
   * small non-initialized, non-constant global and static data.
   */
  .sbss2 ALIGN(4)   :
  {
    *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
    . = ALIGN(4) ;
  } >kseg0_program_mem
  .eh_frame_hdr : { *(.eh_frame_hdr) }
  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }
  .dbg_data (NOLOAD) :
  {
    . += (DEFINED (_DEBUGGER) ? 0x200 : 0x0);
  } >kseg1_data_mem
  /* Persistent data */
  .persist :
  {
    _persist_begin = .;
    *(.persist .persist.*)
    _persist_end = .;
  } >kseg1_data_mem
  .data   :
  {
    _data_begin = . ;
    *(.data .data.* .gnu.linkonce.d.*)
    KEEP (*(.gnu.linkonce.d.*personality*))
    *(.data1)
  } >kseg1_data_mem AT>kseg0_program_mem
  _data_image_begin = LOADADDR(.data) ;
  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
  . = .;
  _gp = ALIGN(16) + 0x7ff0;
  .got ALIGN(4)   :
  {
     *(.got.plt) *(.got)
  } >kseg1_data_mem AT>kseg0_program_mem
  /*
   * We want the small data sections together, so single-instruction offsets
   * can access them all, and initialized data all before uninitialized, so
   * we can shorten the on-disk segment size.
   */
  .sdata ALIGN(4)   :
  {
    _sdata_begin = . ;
    *(.sdata .sdata.* .gnu.linkonce.s.*)
    _sdata_end = . ;
  } >kseg1_data_mem AT>kseg0_program_mem
  .lit8           :
  {
    *(.lit8)
  } >kseg1_data_mem AT>kseg0_program_mem
  .lit4           :
  {
    *(.lit4)
  } >kseg1_data_mem AT>kseg0_program_mem
  . = ALIGN (4) ;
  _data_end = . ;
  _bss_begin = . ;
  .sbss ALIGN(4)   :
  {
    _sbss_begin = . ;
    *(.dynsbss)
    *(.sbss .sbss.* .gnu.linkonce.sb.*)
    *(.scommon)
    _sbss_end = . ;
  } >kseg1_data_mem
  .bss   :
  {
    *(.dynbss)
    *(.bss .bss.* .gnu.linkonce.b.*)
    *(COMMON)
    /*
     * 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(4) ;
  } >kseg1_data_mem
  . = ALIGN(4) ;
  _end = . ;
  _bss_end = . ;
  /* Heap allocating takes a chunk of memory following BSS */
  .heap ALIGN(8) :
  {
    _heap = . ;
    . += _min_heap_size ;
    . = ALIGN(8);
  } >kseg1_data_mem
  /* Stack allocation follows the heap */
  .stack ALIGN(8) :
  {
    _splim = . ;
    _SPLIM = . ;
    . += _min_stack_size ;
    . = ALIGN(8);
  } >kseg1_data_mem
    
  /*
   * RAM functions go at the end of our stack and heap allocation.
   * Alignment of 2K required by the boundary register (BMXDKPBA).
   */
  .ramfunc ALIGN(2K) :
  {
    _ramfunc_begin = . ;
    *(.ramfunc  .ramfunc.*)
    . = ALIGN(4) ;
    _ramfunc_end = . ;
  } >kseg1_data_mem AT>kseg0_program_mem
  _ramfunc_image_begin = LOADADDR(.ramfunc) ;
  _ramfunc_length = SIZEOF(.ramfunc) ;
  _bmxdkpba_address = _ramfunc_begin - ORIGIN(kseg1_data_mem) ;
  _bmxdudba_address = LENGTH(kseg1_data_mem) ;
  _bmxdupba_address = LENGTH(kseg1_data_mem) ;
  
    /* RESET SAFE SECTION TO BE LOCATED AT */
  .reset_safe():
  {
      KEEP(*(.reset_safe))
  } >reset_safe_area
  .calib_request():
  {
      KEEP(*(.calib_request))
  } >calibration_request
  /*
   * The actual top of stack should include the gap between the stack
   * section and the beginning of the .ramfunc section caused by the
   * alignment of the .ramfunc section minus 1 word.  If RAM functions
   * do not exist, then the top of the stack should point to the end of
   * the data memory.
   */
  _stack = (_ramfunc_length > 0)
         ? _ramfunc_begin - 4
         : ORIGIN(kseg1_data_mem) + LENGTH(kseg1_data_mem) ;
  ASSERT((_min_stack_size + _min_heap_size) <= (_stack - _heap),
    "Not enough space to allocate both stack and heap.  Reduce heap and/or stack size.")
    /* The .pdr section belongs in the absolute section */
    /DISCARD/ : { *(.pdr) }
    /* We don't load .reginfo onto the target, so don't locate it
     * in real memory
     */
    /DISCARD/ : { *(.reginfo) }
  /* 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) }
  /DISCARD/ : { *(.rel.dyn) }
  /DISCARD/ : { *(.note.GNU-stack) }
}

PIC32 Soft Reset to Application Start

That’s a bit complex to look through in detail, and does not show what your start up code is doing. Take a more pragmatic approach and step through the C start up code while it is executing. You don’t need to step through each cycle of the zeroing out loop, just see what the start address is and what the end address is (the address the loop will terminate at). If you are using heap_3 then it is likely your heap is not being cleared to zero, but the data structures used by the heap implementation (which will be the C compilers implementation, not a FreeRTOS implementation) will be. All the same, it might be worth manually memset’ing the heap to zero on reset to. You should be able to get the start and end of the heap as linker variables.

PIC32 Soft Reset to Application Start

yes, the CRT0 code probably inits all the memory that it knows about, but I bet that the heap used by FreeRTOS is not initialized.  When the app starts back up, any state not explicitly initialize by will remain. just zero out the freeRTOS heap area and see what happens.

PIC32 Soft Reset to Application Start

Turns out I am completely wrong. When I was doing a jump or initializing C, some interrupt flags were set for some reason. one of those happens to be an interrupt which could cause me to think this was happening. feel pretty ridiculous. thanks for the help though.