Electronic – Saving a variable between reboots on a microcontroller without using flash memory

bootloadercmicrocontrollertexas instruments

I am developing a bootloader and have some trouble jumping from the bootloader to the application. As far as I can see the problem is because I am doing certain initializations in the bootloader which are then also performed in the application. I posted a question about this on the TI forums (https://e2e.ti.com/support/microcontrollers/other/f/908/p/894608/3308592#3308592) but I am not getting any responses so I am looking for another way to fix it.
My idea now is to start the bootloader, download the application image, save it to flash memory and then instead of jumping to the application I want to restart the device and jump to the application before any sort of initialization has been done. I am not sure if this will work but I have been stuck on the jump from bootloader to application for a while now and finally want to make some progress.

For this to work the bootloader will have to somehow know it should jump to the application before any initialization.
As far as I know it is not possible to simply write to a single byte somewhere in flash memory because it will require erasing a whole block and the block size on the device I am using is 8KB large. It seems like a waste to reserve 8KB on a device, just so I can save a simple value to pass on between system resets.

Does anyone have any tips on how I could accomplish this?

I am developing for the CC2652R1F using Code Composer Studio along with the TI v18.12.5.LTS compiler.

EDIT:
I tried using kkrambo's situation but it seems not to be working correctly.
I edited my linker command file to include a .boot_cfg section in RAM with a size of four. I put the new linker command file further below.
I then created a global variable uint32_t config in my file along with #pragma DATA_SECTION(config, ".boot_cfg"); to link it to that section, but I am not sure if I did that correctly. After that I tried reading the value, then changing and then resetting the system to see if it worked.
This is the code I used:

    if (config == 0) {
        GPIO_write(CONFIG_GPIO_LED_RED, CONFIG_GPIO_LED_ON);
        sleep(2);
        config = 2;
        SysCtrlSystemReset();
    } else {
        GPIO_write(CONFIG_GPIO_LED_GREEN, CONFIG_GPIO_LED_ON);
        while(1) {}
    }

When I just flash the bootloader so I would not have to reset the device using the debugger because I think it might reset the power I see the red light turn on, then the value should change and the system should reset and then hopefully I would see the green light go on, but neither lights turned on. So I think the SysCtrlSystemReset() function might not be working probably.
So I tried going through the bootloader step by step, then after the config = 2; statement I used the CPU reset button in the IDE to go back to the beginning, but then when I come back to the if statement the value is still 0.

Linker Command File:

--stack_size=1024   /* C stack is also used for ISR stack */

--heap_size=256

/* Retain interrupt vector table variable                                    */
--retain=g_pfnVectors
/* Override default entry point.                                             */
--entry_point ResetISR
/* Allow main() to take args                                                 */
--args 0x8
/* Suppress warnings and errors:                                             */
/* - 10063: Warning about entry point not being _c_int00                     */
/* - 16011, 16012: 8-byte alignment errors. Observed when linking in object  */
/*   files compiled using Keil (ARM compiler)                                */
--diag_suppress=10063,16011,16012

#define FLASH_BASE              0x0
#define FLASH_SIZE              0x58000
#define RAM_BASE                0x20000000
#define RAM_CFG_BASE            0x20013FFC
#define RAM_CFG_SIZE            0x4 // reserve 4 bytes of ram for bootloader config
#define RAM_SIZE                0x14000 - RAM_CFG_SIZE
#define GPRAM_BASE              0x11000000
#define GPRAM_SIZE              0x2000

#define FLASH_START             FLASH_BASE

#define PAGE_SIZE               0x2000

#define BOOT_BASE               0x50000
#define BOOT_END                FLASH_CCFG_START - 1
#define BOOT_SIZE               ((BOOT_END) - (BOOT_BASE) + 1)

#define APP_BASE                0x0
#define APP_END                 BOOT_BASE
#define APP_SIZE                BOOT_BASE

#define FLASH_CCFG_START        0x00057FA8
#define FLASH_CCFG_END          (FLASH_START + FLASH_SIZE - 1)
#define FLASH_CCFG_SIZE         ((FLASH_CCFG_END) - (FLASH_CCFG_START) + 1)

/* System memory map */

MEMORY
{

    BOOT (RWX)  : origin = BOOT_BASE, length = BOOT_SIZE

    APP (RWX) : origin = APP_BASE, length = APP_SIZE

    FLASH_CCFG (RX) : origin = FLASH_CCFG_START, length = FLASH_CCFG_SIZE

    /* Application uses internal RAM for data */
    SRAM (RWX) : origin = RAM_BASE, length = RAM_SIZE
    RAM_CFG (RWX) : origin = RAM_CFG_BASE, length = RAM_CFG_SIZE
    /* Application can use GPRAM region as RAM if cache is disabled in the CCFG
    (DEFAULT_CCFG_SIZE_AND_DIS_FLAGS.SET_CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM = 0) */
    GPRAM (RWX): origin = GPRAM_BASE, length = GPRAM_SIZE
}



/* Create global constant that points to top of stack */
/* CCS: Change stack size under Project Properties    */
__STACK_TOP = __stack + __STACK_SIZE;


/* Section allocation in memory */

SECTIONS
{
    .intvecs        :   > BOOT_BASE
    .text           :   > BOOT
    .TI.ramfunc     : {} load=BOOT, run=SRAM, table(BINIT)
    .const          :   > BOOT
    .constdata      :   > BOOT
    .rodata         :   > BOOT
    .binit          :   > BOOT
    .cinit          :   > BOOT
    .pinit          :   > BOOT
    .init_array     :   > BOOT
    .emb_text       :   > BOOT
    .ccfg           :   > FLASH_CCFG (HIGH)

    .boot_cfg       :   > RAM_CFG
    .vtable         :   > SRAM
    .vtable_ram     :   > SRAM
     vtable_ram     :   > SRAM
    .data           :   > SRAM
    .bss            :   > SRAM
    .sysmem         :   > SRAM
    .stack          :   > SRAM (HIGH)
    .nonretenvar    :   > SRAM

    .gpram          :   > GPRAM

}

Best Answer

You can create a custom section in RAM that does not get initialized during startup so the values from before the soft reset or jump remain afterwards. This works only for soft resets and jumps. If power is removed (hard reset) then the values in this section will be uninitialized.

Your system has startup code that sets up the C runtime environment before calling main. Your project may be using the default startup code provided by your toolchain. The default startup code likely initializes the .bss section to zero and initializes the variables in the .data section by copying from the .rodata section. If your bootloader and application are compiled as separate projects then they each have their own startup code, .bss, .data, and .rodata sections sections.

Your project may also be using the default linker script provided by your toolchain. The default linker script provides for the memory sections including .bss, .data, and .rodata. You can customize the linker script for your projects and add a custom section named .myspecialsection. You can locate .myspecialsection at a fixed address and size so that both the bootloader and application use the same memory for this section. You can make it so that .myspecialsection is not initialized by the startup code. And you can statically allocate variables and locate them in .myspecialsection. Now the bootloader or application can write a value into .myspecialsection and soft reset or jump to the other program and the values in .myspecialsection will maintain their values. This is how you can pass data values between the bootloader and application or vice-versa.

Read the linker manual to get specifics about how to create a section, how to locate and size it, how to make it uninitialized, and how to locate variables in the section.

Also, consider using a checksum on the data in .myspecialsection to avoid using the data when it is uninitialized.