2

我的应用程序有许多模块,每个模块都需要将一些变量存储在片外非易失性存储器中。为了使读取和写入更容易,我试图将它们收集到一个连续的 RAM 区域中,以便 NVM 驱动程序在与 NVM 设备通信时可以寻址单个内存块。

为此,我创建了一个自定义链接器脚本,其中包含以下部分定义。

.nvm_fram :
{
    /* Include the "nvm_header" input section first. */    
    *(.nvm_header)
    /* Include all other input sections prefixed with "nvm_" from all modules. */    
    *(.nvm_*)

    /* Allocate a 16 bit variable at the end of the section to hold the CRC. */
    . = ALIGN(2);
    _gld_NvmFramCrc = .;
    LONG(0);
} > data
_GLD_NVM_FRAM_SIZE = SIZEOF(.nvm_fram);

data区域使用 Microchip 为目标器件提供的标准定义在 MEMORY 部分中定义。

data (a!xr) : ORIGIN = 0x1000, LENGTH = 0xD000

NVM 驱动程序本身就是试图将其变量放在本节中的 C 源文件的一个示例。驱动程序在 NVM 部分的开头保存一个简短的标头结构,以便它可以在将 NVM 设备加载到 RAM 之前验证它的内容。未报告此变量的链接器错误。

// Locate the NVM configuration in the non-volatile RAM section.
nvm_header_t _nvmHeader  __attribute__((section(".nvm_header")));

另一个具有要存储在 .nvm_fram 部分中的变量的模块是通信 (CANopen) 堆栈。这会将模块 ID 和比特率保存在 NVM 中。

// Locate LSS Slave configuration in the non-volatile RAM section.
LSS_slave_config_t _slaveConfig __attribute__((section(".nvm_canopen"))) =
    { .BitRate = DEFAULT_BITRATE, .ModuleId = DEFAULT_MODULEID };

一切都编译得很好,但是当链接器运行时,以下错误会停止构建。

elf-ld.exe: Link Error: attributes for input section '.nvm_canopen' conflict with
output section '.nvm_fram'

重要的是,可以通过 crt 启动使用值初始化变量,如上面的_slaveConfig声明所示,以防 NVM 驱动程序无法从 NVM 设备加载它们(它是空白的,或者软件版本已更改等)。这是导致属性不匹配的原因吗?

这里和 Microchip 论坛上有几个问题,这些问题与从 C 访问链接描述文件中定义的符号有关。其中大部分涉及闪存程序中的值以及如何从 C 访问它们;我知道该怎么做。有一个类似的问题,但这似乎没有解决属性问题,并且由于特定于不同目标处理器的链接器而有点令人困惑。

我已经在线阅读了 Microchip 链接器手册和各种 GCC 链接器文档,但找不到相关部分,因为我并不真正理解错误的含义以及它与我的代码的关系。什么是“输入和输出部分属性”,它们在我的代码中指定的位置,以及如何让它们相互匹配?

4

1 回答 1

0

问题是由于_nvmHeader变量没有在 C 源代码中分配给它的初始值,但_slaveConfig变量有。

这导致链接器从输入节属性推断.nvm_fram输出节未初始化(nbss) 。因此,当它在变量的输入部分.nvm_header中遇到初始化数据时,输入部分属性不匹配:用于未初始化数据,但包含初始化数据。.nvm_canopen_slaveConfig.nvm_fram.nvm_canopen

解决方案是确保要放置在输出部分中的所有.nvm_fram变量在 C 源代码中都具有初始值。

// Type used to hold metadata for the content of the NVM.
typedef struct
{
    void*       NvmBase;    // The original RAM address.
    uint16_t    NvmSize;    // The original NVM section size.
}   nvm_header_t;

// The linker supplies the gld_NVM_FRAM_SIZE symbol as a 'number'.
// This is represented as the address of an array of unspecified 
// length, so that it cannot be implicitly dereferenced, and cast
// to the correct type when used.
extern char GLD_NVM_FRAM_SIZE[];

// The following defines are used to convert linker symbols into values that
// can be used to initialise the _nvmHeader structure below.
#define NVM_FRAM_BASE ((void*)&_nvmHeader)
#define NVM_FRAM_SIZE ((uint16_t)GLD_NVM_FRAM_SIZE)

// Locate the NVM configuration in the non-volatile RAM section.
nvm_header_t _nvmHeader  __attribute__((section(".nvm_header"))) =
{
    .NvmBase = NVM_FRAM_BASE, .NvmSize = NVM_FRAM_SIZE
};

因此,答案是输出段属性可能部分由该段所在的存储区域以及分配给它的第一个输入段的属性确定。已初始化和未初始化的 C 变量具有不同的输入段属性,因此不能位于同一输出段内。

于 2015-04-27T17:51:37.740 回答