Incruste las notas breves del programador: duplicaci贸n de secci贸n en la memoria del microcontrolador

Condiciones iniciales


Hay un dispositivo basado en un microcontrolador (por ejemplo, se tomar谩 stm32f405rgt6). Cuando se enciende, ajusta sus perif茅ricos seg煤n las preferencias del usuario o la configuraci贸n predeterminada. El usuario puede cambiar la configuraci贸n durante la operaci贸n del dispositivo (por regla general, solo durante la integraci贸n en el complejo) a trav茅s de una de las interfaces posibles (men煤 CLI o utilidad para configurar los par谩metros de operaci贸n que funcionan a trav茅s del protocolo binario). Despu茅s de configurar los par谩metros, el usuario guarda la configuraci贸n con un comando especial (tambi茅n a trav茅s de una de las posibles interfaces).

Tarea


  • Para reducir el tiempo que lleva acceder a las variables de configuraci贸n del dispositivo durante la operaci贸n, debe mantener los valores actuales en la RAM (por lo general, la cantidad de dichos datos var铆a de una docena de variables a 3 kilobytes, seg煤n el dispositivo).
  • Es necesario almacenar la configuraci贸n del usuario por duplicado en el flash del microcontrolador.
  • Cada instancia de configuraci贸n del usuario debe terminar con CRC32 inmediatamente despu茅s de la carga 煤til.
  • Para cada instancia de configuraci贸n del usuario, se utiliza una p谩gina separada en la memoria flash (incluso si los datos 煤tiles son 2 kb, y las p谩ginas se dividen por 128 kb, entonces toda la p谩gina se da en un bloque)
  • El c贸digo del software del microcontrolador debe almacenar la configuraci贸n predeterminada, que debe ser el bloque de configuraci贸n del usuario si ambos tienen datos corruptos.

Intentos de resolver


GCC (al momento de escribir este art铆culo) no tiene una bandera para obtener una copia de la secci贸n. Complementar el script LD con << l铆neas m谩gicas >> tambi茅n falla. Se podr铆a trabajar con objcopy, pero este enfoque es muy impl铆cito y conduce a errores sutiles.

Decisi贸n


La soluci贸n es crear copias impl铆citas de la entidad deseada (variable, estructura, matriz, etc.) en el c贸digo del usuario, seguido de su ubicaci贸n en la memoria.

Crear una macro para la copia oculta de estructuras


Creemos una macro con la que reservaremos un lugar para la estructura en RAM, 2 instancias de memoria flash en p谩ginas separadas y en el c贸digo de usuario (para valores iniciales).

#define USER_CFG_DATA_STRUCT(TYPE,NAME,...) \
    __attribute__((aligned(4), section (".user_cfg_data_ram_page"))) \
    TYPE NAME = __VA_ARGS__; \
    __attribute__((aligned(4), section (".user_cfg_data_flash_default_page"))) \
    TYPE flash_default_page_##NAME = __VA_ARGS__; \
    __attribute__((aligned(4), section (".user_cfg_data_flash_page_1"))) \
    TYPE flash_page_1_##NAME = __VA_ARGS__; \
    __attribute__((aligned(4), section (".user_cfg_data_flash_page_2"))) \
    TYPE flash_page_2_##NAME = __VA_ARGS__;

Usando una macro, se crear谩n 4 instancias de la estructura con diferentes nombres. En el c贸digo del proyecto (con la excepci贸n del sistema para trabajar con p谩ginas de memoria de configuraci贸n del usuario), todos los m贸dulos deben usar una estructura sin prefijo (el nombre especificado en la macro) que estar谩 en la RAM (ser谩 posible cambiar los m贸dulos que son responsables de la interacci贸n del usuario. El resto solo debe leer de ella).

Un ejemplo de uso de una macro en el c贸digo de usuario:

typedef struct _test_st {
    uint32_t a1;
    uint32_t a2;
} test_st_t;

USER_CFG_DATA_STRUCT(test_st_t, name_st, {
    .a1 = 1,
    .a2 = 2
})

Despu茅s de la instancia de la estructura name_st est谩 disponible desde el c贸digo del proyecto.

Crear macros para la copia oculta de otras entidades.


Para crear una variable, solo necesita hacer la macro sustituci贸n para crear estructuras.
Para matrices, agregue el n煤mero de elementos.

#define USER_CFG_DATA_VAR USER_CFG_DATA_STRCUT
#define USER_CFG_DATA_ARRAY(TYPE,NAME,SIZE,...) \
    __attribute__((aligned(4), section (".user_cfg_data_ram_page"))) \
    TYPE NAME[SIZE] = __VA_ARGS__; \
    __attribute__((aligned(4), section (".user_cfg_data_flash_default_page"))) \
    TYPE flash_default_page_##NAME[SIZE] = __VA_ARGS__; \
    __attribute__((aligned(4), section (".user_cfg_data_flash_page_1"))) \
    TYPE flash_page_1_##NAME[SIZE] = __VA_ARGS__; \
    __attribute__((aligned(4), section (".user_cfg_data_flash_page_2"))) \
    TYPE flash_page_2_##NAME[SIZE] = __VA_ARGS__;

El principio de uso es similar al uso de la estructura.

Finalizaci贸n del script LD


Al crear copias de entidades, se indic贸 en qu茅 secciones se encuentran. Ahora debe crear estas secciones en el script LD. Para F4, el script LD aumentado de ST se ver谩 as铆:

Secuencia de comandos LD
MEMORY
{
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K - 256K /* 256   user_cfg */
   USER_CFG_PAGE_1 (rx) : ORIGIN = 0x080C0000, LENGTH = 128K - 4     /* 4  CRC32 */
    USER_CFG_PAGE_2 (rx) : ORIGIN = 0x080E0000, LENGTH = 128K - 4     /* 4  CRC32 */
    CCM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
    RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}

/*       CRC32.
       .      - . */
user_cfg_data_flash_page_1_size = LENGTH(user_cfg_PAGE_1) + 4;
user_cfg_data_flash_page_2_size = LENGTH(user_cfg_PAGE_2) + 4;
user_cfg_data_flash_page_size = user_cfg_data_flash_page_1_size;

/*
 * The '__stack' definition is required by crt0, do not remove it.
 */
__stack = ORIGIN(RAM) + LENGTH(RAM);

_estack = __stack;     /* STM specific definition */

/*
 * Default stack sizes.
 * These are used by the startup in order to allocate stacks
 * for the different modes.
 */

__Main_Stack_Size = 1024 ;

PROVIDE ( _Main_Stack_Size = __Main_Stack_Size ) ;

__Main_Stack_Limit = __stack  - __Main_Stack_Size ;

/*"PROVIDE" allows to easily override these values from an object file or the command line. */
PROVIDE ( _Main_Stack_Limit = __Main_Stack_Limit ) ;

/*
 * There will be a link error if there is not this amount of
 * RAM free at the end.
 */
_Minimum_Stack_Size = 512 ;

/*
 * Default heap definitions.
 * The heap start immediately after the last statically allocated
 * .sbss/.noinit section, and extends up to the main stack limit.
 */
PROVIDE ( _Heap_Begin = _end_noinit ) ;
PROVIDE ( _Heap_Limit = __stack - __Main_Stack_Size ) ;

SECTIONS
{
    .isr_vector :
    {
        KEEP(*(.isr_vector))         /* Interrupt vectors */
        KEEP(*(.cfmconfig))            /* Freescale configuration words */
        *(.after_vectors .after_vectors.*)    /* Startup code and ISR */
        . = ALIGN(4);
    } >FLASH

    .inits :
    {
        . = ALIGN(4);

        /*
         * These are the old initialisation sections, intended to contain
         * naked code, with the prologue/epilogue added by crti.o/crtn.o
         * when linking with startup files. The standalone startup code
         * currently does not run these, better use the init arrays below.
         */
        KEEP(*(.init))
        KEEP(*(.fini))

        . = ALIGN(4);

        /*
         * The preinit code, i.e. an array of pointers to initialisation
         * functions to be performed before constructors.
         */
        PROVIDE_HIDDEN (__preinit_array_start = .);

        /*
         * Used to run the SystemInit() before anything else.
         */
        KEEP(*(.preinit_array_sysinit .preinit_array_sysinit.*))

        /*
         * Used for other platform inits.
         */
        KEEP(*(.preinit_array_platform .preinit_array_platform.*))

        /*
         * The application inits. If you need to enforce some order in
         * execution, create new sections, as before.
         */
        KEEP(*(.preinit_array .preinit_array.*))

        PROVIDE_HIDDEN (__preinit_array_end = .);

        . = ALIGN(4);

        /*
         * The init code, i.e. an array of pointers to static constructors.
         */
        PROVIDE_HIDDEN (__init_array_start = .);
        KEEP(*(SORT(.init_array.*)))
        KEEP(*(.init_array))
        PROVIDE_HIDDEN (__init_array_end = .);

        . = ALIGN(4);

        /*
         * The fini code, i.e. an array of pointers to static destructors.
         */
        PROVIDE_HIDDEN (__fini_array_start = .);
        KEEP(*(SORT(.fini_array.*)))
        KEEP(*(.fini_array))
        PROVIDE_HIDDEN (__fini_array_end = .);
        . = ALIGN(4);

    } >FLASH

    /*
     * For some STRx devices, the beginning of the startup code
     * is stored in the .flashtext section, which goes to FLASH.
     */
    .flashtext :
    {
        . = ALIGN(4);
        *(.flashtext .flashtext.*)    /* Startup code */
        . = ALIGN(4);
    } >FLASH


    /*
     * The program code is stored in the .text section,
     * which goes to FLASH.
     */
    .text :
    {
        . = ALIGN(4);

        *(.text .text.*)            /* all remaining code */

        *(.rodata .rodata.*)         /* read-only data (constants) */

        *(vtable)                    /* C++ virtual tables */

        KEEP(*(.eh_frame*))

        /*
         * Stub sections generated by the linker, to glue together
         * ARM and Thumb code. .glue_7 is used for ARM code calling
         * Thumb code, and .glue_7t is used for Thumb code calling
         * ARM code. Apparently always generated by the linker, for some
         * architectures, so better leave them here.
         */
        *(.glue_7)
        *(.glue_7t)
    } >FLASH

    .user_cfg_data_flash_default_page :
    {
        . = ALIGN(4);
        user_cfg_data_flash_default_page_start = .;
        KEEP(*(.user_cfg_data_flash_default_page .user_cfg_data_flash_default_page.*))
        . = ALIGN(4);
        user_cfg_data_flash_default_page_stop = .;
    } >FLASH

    /* ARM magic sections */
    .ARM.extab :
       {
       *(.ARM.extab* .gnu.linkonce.armextab.*)
       } > FLASH

       __exidx_start = .;
       .ARM.exidx :
       {
       *(.ARM.exidx* .gnu.linkonce.armexidx.*)
       } > FLASH
       __exidx_end = .;

    . = ALIGN(4);
    _etext = .;
    __etext = .;

    /*
     * This address is used by the startup code to
     * initialise the .data section.
     */
    _sidata = _etext;

    /* MEMORY_ARRAY */
    /*
    .ROarraySection :
    {
         *(.ROarraySection .ROarraySection.*)
    } >MEMORY_ARRAY
    */

    /*
     * The initialised data section.
     * The program executes knowing that the data is in the RAM
     * but the loader puts the initial values in the FLASH (inidata).
     * It is one task of the startup to copy the initial values from
     * FLASH to RAM.
     */
    .data  : AT ( _sidata )
    {
        . = ALIGN(4);

        /* This is used by the startup code to initialise the .data section */
        _sdata = . ;            /* STM specific definition */
        __data_start__ = . ;
        *(.data_begin .data_begin.*)

        *(.data .data.*)

        *(.data_end .data_end.*)

        *(.ramfunc*)
        . = ALIGN(4);

        /* This is used by the startup code to initialise the .data section */
        _edata = . ;            /* STM specific definition */
        __data_end__ = . ;

    } >RAM


    /*
     * The uninitialised data section. NOLOAD is used to avoid
     * the "section `.bss' type changed to PROGBITS" warning
     */
    .bss (NOLOAD) :
    {
        . = ALIGN(4);
        __bss_start__ = .;         /* standard newlib definition */
        _sbss = .;              /* STM specific definition */
        *(.bss_begin .bss_begin.*)

        *(.bss .bss.*)
        *(COMMON)

        *(.bss_end .bss_end.*)
        . = ALIGN(4);
        __bss_end__ = .;        /* standard newlib definition */
        _ebss = . ;             /* STM specific definition */
    } >RAM

    .user_cfg_data_ram_page :
    {
        . = ALIGN(4);
        user_cfg_data_ram_page_start = .;
        KEEP(*(.user_cfg_data_ram_page .user_cfg_data_ram_page.*))
        . = ALIGN(4);
        user_cfg_data_ram_page_stop = .;
    } > CCM

    user_cfg_data_ram_page_size = user_cfg_data_ram_page_stop - user_cfg_data_ram_page_start;

    .user_cfg_data_page_1 :
    {
        . = ALIGN(4);
        user_cfg_data_flash_page_1_start = .;
         KEEP(*(.user_cfg_data_flash_page_1 .user_cfg_data_flash_page_1.*))
        . = ALIGN(4);
        user_cfg_data_flash_page_1_stop = .;
    } > user_cfg_PAGE_1

    .user_cfg_data_page_2 :
    {
        . = ALIGN(4);
        user_cfg_data_flash_page_2_start = .;
         KEEP(*(.user_cfg_data_flash_page_2 .user_cfg_data_flash_page_2.*))
        . = ALIGN(4);
        user_cfg_data_flash_page_2_stop = .;
    } > user_cfg_PAGE_2

    .noinit (NOLOAD) :
    {
        . = ALIGN(4);
        _noinit = .;

        *(.noinit .noinit.*)

         . = ALIGN(4) ;
        _end_noinit = .;
    } > RAM

    /* Mandatory to be word aligned, _sbrk assumes this */
    PROVIDE ( end = _end_noinit ); /* was _ebss */
    PROVIDE ( _end = _end_noinit );
    PROVIDE ( __end = _end_noinit );
    PROVIDE ( __end__ = _end_noinit );

    /*
     * Used for validation only, do not allocate anything here!
     *
     * This is just to check that there is enough RAM left for the Main
     * stack. It should generate an error if it's full.
     */
    ._check_stack :
    {
        . = ALIGN(4);
        . = . + _Minimum_Stack_Size ;
        . = ALIGN(4);
    } >RAM


    /* After that there are only debugging sections. */

    /* This can remove the debugging information from the standard libraries */
    /*
    DISCARD :
    {
     libc.a ( * )
     libm.a ( * )
     libgcc.a ( * )
     }
     */

    /* 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) }
}

En este ejemplo, la estructura de configuraci贸n no se encuentra en el 谩rea de RAM, sino en CCMRAM, para mejorar el rendimiento. Tambi茅n noto que los bloques de datos de configuraci贸n del usuario son las 煤ltimas 2 p谩ginas flash, cada una de las cuales tiene 128 kb.

Inicializaci贸n y control de copias.


El cuidado de lo que se encuentra en la RAM recae en el programador. Antes de utilizar datos de esta 谩rea por primera vez, debe inicializar el 谩rea. En flash, al cargar el programa en el microcontrolador, los datos se grabar谩n previamente con los valores iniciales especificados en el c贸digo del programa. Esto har谩 copias no v谩lidas de los bloques de configuraci贸n del usuario en p谩ginas separadas si terminan en CRC32 (ya que no se realiza ning煤n c谩lculo de CRC32. Su c贸digo puede hacerlo. Tambi茅n hasta el primer uso de los par谩metros de configuraci贸n).

Para escribir un m贸dulo para trabajar con bloques de configuraci贸n, necesitar谩 usar las variables del script LD. Se requerir谩 lo siguiente:

extern uint32_t user_cfg_data_flash_default_page_start;
extern uint32_t user_cfg_data_flash_page_1_start;
extern uint32_t user_cfg_data_flash_page_2_start;
extern uint32_t user_cfg_data_ram_page_start;

extern uint32_t user_cfg_data_flash_page_size;
extern uint32_t user_cfg_data_ram_page_size;

Adem谩s, no olvide que user_cfg_data_flash_page_size y user_cfg_data_ram_page_size se pueden asignar como valores normales. Pero user_cfg_data_flash_page_1_start y otras variables que almacenan la direcci贸n deben especificarse mediante &.

Source: https://habr.com/ru/post/undefined/


All Articles