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.

[参考译文] MSPM0G1107:将仿真 EEPROM 段放置在闪存基地址时是否需要引导加载程序桩?

Guru**** 2388540 points
Other Parts Discussed in Thread: SEGGER
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1457952/mspm0g1107-is-a-bootloader-stump-needed-when-placing-the-emulated-eeprom-section-at-the-flash-base-address

器件型号:MSPM0G1107
主题中讨论的其他器件:SEGGER

工具与软件:

你好

我根据 TI 的应用手册实施了一个运行良好的仿真 EEPROM。 在链接器文件中、我 为仿真 EEPROM 保留了一个存储器区域。 由于较低的32kB 闪存地址空间支持较高的擦除/编程耐久性、 我将仿真 EEPROM 放置在闪存基址(0x0000.0000)、而主程序放置在闪存地址0x0000.1400。当调试器被连接时、这运行正常。 如果没有连接调试器、主程序将无法启动。

经过一些研究,我发现,调试器的设置或更精确的调试软件(SEGGER Ozone )处理矢量表的重定位,并正确设置程序计数器。

为解决此问题、我添加了一个具有引导加载程序堆栈的存储器区域、该堆栈中包含一个带有堆栈指针和复位处理程序的小矢量表。 然后、该复位处理程序执行到主应用程序复位处理程序的跳转。 这按预期运行。 正如我理解的那样、控制器默认期望在闪存基址上有一个带有堆栈指针和复位处理程序的矢量表。  

由于仿真 EEPROM 的起始地址需要位于某个页地址、因此我为引导加载程序凸点保留了一个完整的闪存页、尽管它不需要那么多的闪存。  

我宁愿仅为引导加载程序建立额外的存储器部分、并且具有自己的引导矢量表、而只具有基于地址0x0000.0000的主矢量表。 还有什么其他解决方案、那么我目前正在使用的解决方案吗? 我可以在两个非连续闪存区域之间以某种方式拆分我的主程序吗?

我的链接器文件:

/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size  = 0;      /* required amount of heap  */
_Min_Stack_Size = 0x80; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
    FLASH_BOOT          (RX)  : ORIGIN = 0x00000000, LENGTH = 1K
    EMULATED_EEPROM     (RW)  : ORIGIN = 0x00000400, LENGTH = 4K
    FLASH               (RX)  : ORIGIN = 0x00001400, LENGTH = 123K    
    SRAM                (RWX) : ORIGIN = 0x20200000, LENGTH = 32K
    BCR_CONFIG          (R)   : ORIGIN = 0x41C00000, LENGTH = 0x00000080
    BSL_CONFIG          (R)   : ORIGIN = 0x41C00100, LENGTH = 0x00000080

}

/* Note: SRAM length must match MPPC/MEMSS config! Please edit it manually. */

REGION_ALIAS("REGION_TEXT", FLASH);
REGION_ALIAS("REGION_PREINIT_ARRAY", FLASH);
REGION_ALIAS("REGION_INIT_ARRAY", FLASH);
REGION_ALIAS("REGION_FINI_ARRAY", FLASH);
REGION_ALIAS("REGION_BSS", SRAM);
REGION_ALIAS("REGION_NOINIT", SRAM);
REGION_ALIAS("REGION_DATA", SRAM);
REGION_ALIAS("REGION_STACK", SRAM);
REGION_ALIAS("REGION_HEAP", SRAM);
REGION_ALIAS("REGION_TEXT_RAM", SRAM);
REGION_ALIAS("REGION_ARM_EXIDX", FLASH);
REGION_ALIAS("REGION_ARM_EXTAB", FLASH);

/* Define output sections */
SECTIONS
{

    /* section for the boot interrupt vector area                                 */
    PROVIDE (_intvecs_boot_base_address =
        DEFINED(_intvecs_boot_base_address) ? _intvecs_boot_base_address : 0x00000000);

    .intvecs_boot (_intvecs_boot_base_address) : AT (_intvecs_boot_base_address) {
        . = ALIGN(0x4);
        KEEP (*(.intvecs_boot))
        . = ALIGN(0x4);
    } > FLASH_BOOT

    __emulated_eeprom_base_address = ORIGIN(EMULATED_EEPROM);

    .emulated_eeprom : {
        . = ALIGN(0x8);
        KEEP (*(.emulated_eeprom))
        . = ALIGN(0x8);
    } > EMULATED_EEPROM    

    __program_base_address = ORIGIN(FLASH);

    /* section for the interrupt vector area                                 */
    PROVIDE (_intvecs_base_address =
        DEFINED(_intvecs_base_address) ? _intvecs_base_address : __program_base_address);

    .intvecs (_intvecs_base_address) : AT (_intvecs_base_address) {
        KEEP (*(.intvecs))
    } > REGION_TEXT

    PROVIDE (_vtable_base_address =
        DEFINED(_vtable_base_address) ? _vtable_base_address : 0x20200000);

    .vtable (_vtable_base_address) : AT (_vtable_base_address) {
        KEEP (*(.vtable))
    } > REGION_DATA

   .text : {
        CREATE_OBJECT_SYMBOLS
        KEEP (*(.text))
        . = ALIGN(0x8);
        *(.text.*)
        . = ALIGN(0x8);
        KEEP (*(.ctors))
        . = ALIGN(0x8);
        KEEP (*(.dtors))
        . = ALIGN(0x8);
        KEEP (*(.init))
        . = ALIGN(0x8);
        KEEP (*(.fini*))
        . = ALIGN(0x8);
    } > REGION_TEXT AT> REGION_TEXT

    .ramfunc : {
        __ramfunct_load__ = LOADADDR (.ramfunc);
        __ramfunct_start__ = .;
       *(.ramfunc)
       . = ALIGN(0x8);
       __ramfunct_end__ = .;
    } > REGION_TEXT_RAM AT> REGION_TEXT

    .rodata : {
        *(.rodata)
        . = ALIGN(0x8);
        *(.rodata.*)
        . = ALIGN(0x8);
    } > REGION_TEXT AT> REGION_TEXT

    .preinit_array : {
        PROVIDE_HIDDEN (__preinit_array_start = .);
        KEEP (*(.preinit_array*));
        PROVIDE_HIDDEN (__preinit_array_end = .);
    } > REGION_PREINIT_ARRAY AT> REGION_TEXT

    .init_array : {
        . = ALIGN(0x8);
        PROVIDE_HIDDEN (__init_array_start = .);
        KEEP (*(SORT(.init_array.*)))
        KEEP (*(.init_array*))
        PROVIDE_HIDDEN (__init_array_end = .);
    } > REGION_INIT_ARRAY AT> REGION_TEXT

    .fini_array : {
        . = ALIGN(0x8);
        PROVIDE_HIDDEN (__fini_array_start = .);
        KEEP (*(SORT(.fini_array.*)))
        KEEP (*(.fini_array*))
        PROVIDE_HIDDEN (__fini_array_end = .);
    } > REGION_FINI_ARRAY AT> REGION_TEXT


    .ARM.exidx : {
    	. = ALIGN(0x8);
        __exidx_start = .;
        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
        . = ALIGN(0x8);
        __exidx_end = .;
    } > REGION_ARM_EXIDX AT> REGION_ARM_EXIDX

    .ARM.extab : {
        KEEP (*(.ARM.extab* .gnu.linkonce.armextab.*))
         . = ALIGN(0x8);
    } > REGION_ARM_EXTAB AT> REGION_ARM_EXTAB

    __etext = .;

    .data : {
        __data_load__ = LOADADDR (.data);
        __data_start__ = .;
        KEEP (*(.data))
        KEEP (*(.data*))
        . = ALIGN (8);
        __data_end__ = .;
    } > REGION_DATA AT> REGION_TEXT

    .bss : {
        __bss_start__ = .;
        *(.shbss)
        KEEP (*(.bss))
        *(.bss.*)
        *(COMMON)
        . = ALIGN (8);
        __bss_end__ = .;
    } > REGION_BSS AT> REGION_BSS

    .noinit : {
        /* place all symbols in input sections that start with .noinit */
        KEEP(*(*.noinit*))
        . = ALIGN (8);
    } > REGION_NOINIT AT> REGION_NOINIT

    .heap : {
        __heap_start__ = .;
        end = __heap_start__;
        _end = end;
        __end = end;
        KEEP (*(.heap))
        __heap_end__ = .;
        __HeapLimit = __heap_end__;
    } > REGION_HEAP AT> REGION_HEAP

    .stack (NOLOAD) : ALIGN(0x8) {
        _stack = .;
        KEEP(*(.stack))
    } > REGION_STACK AT> REGION_STACK

    .BCRConfig :
    {
        KEEP(*(.BCRConfig))
    } > BCR_CONFIG

    .BSLConfig :
    {
        KEEP(*(.BSLConfig))
    } > BSL_CONFIG

    
    __StackTop = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK);
    PROVIDE(__stack = __StackTop);
}

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Roland:

    我在这里有几条评论。

    首先介绍最简单的方法、正确的是器件 在存储器的前两个位置需要一个堆栈指针和复位向量。

    用于在两个非连续闪存区域之间拆分主程序.... 我相信你可以,但这不是我所经历的东西。 虽然我们可以开始理解这一点、但我有一项更新、这意味着您无需担心这一点:

    我们决定对闪存的额外写入擦除周期方面的描述方式进行更新、以便更好地反映器件的实际行为、并为像您这样的客户提供以下便利:

    在任何地址处32KB 的器件闪存可以具有扩展的写擦除周期、不一定是存储器的前32kB。  因此、编程人员可选择使用任何存储器扇区(高达32kB)来实现 EEPROM 仿真等功能、并获取额外的写入擦除周期。 您所需要做的就是确保最多只对32kB 内存使用100k 周期、对其余内存使用10k 周期。

    此信息尚未发布到我们的数据表中、但目前正在添加、因此应该很快会在其中反映出来。

    所有这些假设都是为了让您能够将 EEPROM 仿真区域放置在前32kB 存储器之外、无论您想放置在哪里、您仍然能够获得更高的耐写能力(高达32kB 的闪存)。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Dylan

    感谢您的快速回复。 这是个好消息、它可以大幅简化链接器文件。 我不是真的渴望知道如何分割主程序在两个闪存区域;)。  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    还有另外一个问题、我在数据表或参考手册中找不到任何相关信息。 不确定这里是否适合提问、但您能告诉我器件在闪存单元损坏时的反应如何吗?  

    我从其他微控制器知道、损坏的闪存单元有时会标记为全零。 是这样吗? 是否有可能写入损坏的闪存单元?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    哈哈,我确定,这可能会是... 影响。 也很高兴我们不必以这种方式解决这个问题!

    对于第二个问题、 我们在器件中用于检测闪存损坏的机制是 ECC。 当您写入闪存的 ECC 校正部分时、ECC 被计算得出并存储到存储器的另一部分。 当您从闪存的 ECC 校正部分读取时、会再次计算 ECC、并与保存的值进行比较。 如果两个 ECC 不同、则触发 NMI。 在 NMI 处理程序中、您可以选择如何处理闪存 ECC 错误。 因此、您可以选择将其忽略、或执行您喜欢的任何操作。 简而言之、就是当您读取损坏的闪存数据片段时将触发 NMI。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我仍然应该能够写入损坏的闪存数据片段(例如、向其中写入特定模式?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    是的、在检测到位翻转之前、只要 NMI 处理程序允许器件在 ECC 错误之后继续执行程序、您便可以写入闪存数据段。