Какие атрибуты входного раздела требуются для размещения переменной C в выходном разделе памяти данных?

В моем приложении есть несколько модулей, каждый из которых требует, чтобы некоторые переменные были сохранены в энергонезависимой памяти вне кристалла. Чтобы упростить чтение и запись, я пытаюсь собрать их вместе в непрерывную область ОЗУ, чтобы драйвер 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 регион определяется в разделе MEMORY с использованием стандартного определения, предоставленного Microchip для целевого устройства.

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

Одним из примеров исходного файла C, который пытается разместить свои переменные в этом разделе, является сам драйвер NVM. Драйвер сохраняет структуру короткого заголовка в начале раздела NVM, чтобы он мог проверить содержимое устройства NVM перед загрузкой его в ОЗУ. Об этой переменной не сообщалось об ошибке компоновщика.

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

Другой модуль, который имеет переменные для хранения в разделе.nvm_fram, - это стек коммуникаций (CANopen). Это сохраняет идентификатор модуля и битрейт в 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 (он пустой, или версия программного обеспечения изменилась и т. д.). Это то, что вызывает несоответствие атрибутов?

Здесь и на форумах по микрочипам есть несколько вопросов, касающихся доступа к символам, которые определены в скрипте компоновщика из C. Большинство из них касаются значений во флэш-памяти программы и как получить к ним доступ из C; Я знаю, как это сделать. Есть похожий вопрос, но, похоже, он не решает проблему атрибутов и немного сбивает с толку из-за специфичности для компоновщика для другого целевого процессора.

Я прочитал руководство по работе с компоновщиком Microchip и различные документы по компоновщику GCC в Интернете, но не могу найти соответствующие разделы, потому что не совсем понимаю, что означает ошибка и как она связана с моим кодом. Что такое "атрибуты входных и выходных разделов", где они указаны в моем коде и как я могу получить их для соответствия друг другу?

1 ответ

Решение

Проблема связана с _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 имеют разные атрибуты входного раздела и поэтому не могут быть расположены в одном и том же выходном разделе.

Другие вопросы по тегам