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