У меня есть массив структур, которые мне нужны для инициализации во время компиляции (без memset) до 0xFF. Этот массив будет записан как часть программы поверх стираемой флэш-памяти. Установив для него значение 0xFF, он останется стертым после программирования, и приложение сможет использовать его в качестве постоянного хранилища. Я нашел два способа сделать это, один уродливый и один обходной путь. Мне интересно, есть ли другой способ с синтаксисом, который я еще не нашел. Уродливый способ - использовать вложенный инициализатор, устанавливающий каждое поле структуры. Тем не менее, это подвержено ошибкам и немного уродливо. Мой обходной путь состоит в том, чтобы выделить структуру как массив байтов, а затем использовать указатель типа структуры для доступа к данным. Линейные массивы байтов гораздо проще инициализировать ненулевым значением.
Чтобы помочь всем, кто делает то же самое, я включаю используемые атрибуты gcc и часть скрипта компоновщика.
Пример структуры:
struct BlData_t {
uint8_t version[3];
uint8_t reserved;
uint8_t markers[128];
struct AppData_t {
uint8_t version[3];
uint8_t reserved;
uint32_t crc;
} appInfo[512] __attribute__(( packed ));
} __attribute__(( packed ));
Инициализировать до 0xFF, используя лучший из известных мне способов:
// Allocate the space as an array of bytes
// because it's a simpler syntax to
// initialize to 0xFF.
__attribute__(( section(".bootloader_data") ))
uint8_t bootloaderDataArray[sizeof(struct BlData_t)] = {
[0 ... sizeof(struct BlData_t) - 1] = 0xFF
};
// Use a correctly typed pointer set to the
// array of bytes for actual usage
struct BlData_t *bootloaderData = (struct BlData_t *)&bootloaderDataArray;
Инициализация не требуется из-за (NOLOAD)
:
__attribute__(( section(".bootloader_data") ))
volatile const struct BLData_t bootloader_data;
Дополнение к скрипту компоновщика:
.bootloader_data (NOLOAD):
{
FILL(0xFF); /* Doesn't matter because (NOLOAD) */
. = ALIGN(512); /* start on a 512B page boundary */
__bootloader_data_start = .;
KEEP (*(.bootloader_data)) /* .bootloader_data sections */
KEEP (*(.bootloader_data*)) /* .bootloader_data* sections */
. = ALIGN(512); /* end on a 512B boundary to support
runtime erasure, if possible */
__bootloader_data_end = .;
__bootloader_data_size = ABSOLUTE(. - __bootloader_data_start);
} >FLASH
Как использовать начальный адрес, конечный адрес и размер в коде:
extern uint32_t __bootloader_data_start;
extern uint32_t __bootloader_data_end;
extern uint32_t __bootloader_data_size;
uint32_t _bootloader_data_start = (uint32_t)&__bootloader_data_start;
uint32_t _bootloader_data_end = (uint32_t)&__bootloader_data_end;
uint32_t _bootloader_data_size = (uint32_t)&__bootloader_data_size;
Обновлять:
(NOLOAD)
, который говорит загрузчику программы не записывать этот раздел во флэш-память. Я принял этот ответ, чтобы помочь другим осознать мою и, возможно, их ошибку. Даже не программируя секцию, мне вообще не нужно беспокоиться об инициализации.union
, так как они кажутся хорошим решением вопроса, который я задал.Вы можете поиграть с компоновщиком и поместить структуру в раздел, заполненный 0xFF, но с точки зрения компилятора предполагается, что неинициализированная глобальная структура заполнена нулями, и он может попытаться использовать это предположение для определенной оптимизации, и он может привести к неожиданным проблемам.
Объявите массив в исходном коде C. Определите массив в сборке с подходящими директивами для его выравнивания и заполнения байтами 0xFF.
Можете ли вы определить одну структуру со всеми полями, установленными в 0xFF, а затем использовать расширение GCC, чтобы установить все элементы массива в это значение?
Как насчет определения union
вашей структуры с помощью массива соответствующего размера, а затем инициализации члена массива?
Объявите структуру extern
, разрешите ее с помощью скрипта компоновщика и прикажите компоновщику в том же скрипте заполнить пространство 0xFF.
Более сельским подходом является использование инструмента для исправления полученного двоичного или шестнадцатеричного файла, например «srecord».
Как насчет создания экземпляра в файле отдельный.c
, аналогично тому, что вы делаете сейчас, за исключением того, что нет причудливого компоновщика [если он вам не нужен для выравнивания или вам не нужен определенный адрес загрузки для ПЗУ]. Используйте имя один (например, uint8_t bootloaderdata ...
). Затем в каждом другом .c
файле используйте (например) extern struct BlData_t bootloaderdata;
«уродливый способ - использовать вложенный инициализатор, устанавливающий каждое поле структуры». --> с магией макросов можно использовать struct BlData_t D = {FOO(3, 0xFF), 0xFF, FOO(128, 0xFF), ...
@JohnFilleau Я пишу код на стертый флэш-чип. В стертой флэш-памяти все байты инициализированы до 0xFF. Вы можете очистить биты один раз, но вы не можете установить биты, которые были очищены, без повторного стирания флэш-памяти. Итак, я пытался сделать всю область 0xFFs, чтобы программирование эффективно оставило ее стертой. Если бы я написал все нули, я бы никогда не смог использовать эту флэш-память.
@ЕвгенийШ. Я добавил KEEP() в компоновщик, чтобы --gc-sections
не оптимизировал память. Я также видел предложение пометить данные как изменчивые, чтобы gcc также не делал предположений.
@thebusybee Я думаю об этом решении. Я пытаюсь сделать процессор кода независимым. Делать это по-своему означает знать в сценарии компоновщика, сколько памяти требуется коду, и каждому процессору может потребоваться разное количество памяти в зависимости от характеристик чипа. Делая это так, как я, позволяет компоновщику выяснить, насколько большим должен быть раздел, основываясь на количестве данных, которые код объявляет в этом разделе. Идея состоит в том, что каждый процессор может совместно использовать один и тот же раздел сценария компоновщика без знания кода.
@thebusybee Спасибо за сельский подход. Я пытался заставить инструменты работать на меня и упростить сборку.
@CraigEstey Мне определенно нужно выравнивание компоновщика. На некоторых процессорах, которые я использую, я могу стирать всего 512 байт за раз, если память находится в начале флэш-памяти. В других, таких как чипы STM32, я могу стереть только минимум 32 КБ, что фактически означает, что я никогда не смогу стереть эту область. Итак, для некоторых чипов я могу использовать меньшую структуру и стирать ее при необходимости. На других чипах мне нужно сделать площадь достаточно большой, чтобы хватило на весь срок службы продукта.
@chux-ReinstateMonica Я избегаю «уродливого» решения, потому что оно подвержено ошибкам. Если кто-то не понял это точно, gcc заполнит нулями. Я собираюсь попробовать решения union и NOLOAD ниже сегодня днем, а затем доложу, обновив ответ и приняв решение.
@Harvey Хм, «... потому что это подвержено ошибкам». --> не более, чем альтернативы.
Я бы использовал union
вашей структуры вместе с массивом правильного размера, а затем инициализировал элемент массива.
union {
struct BlData_t data;
uint8_t bytes[sizeof(struct BlData_t)];
} data_with_ff = {
.bytes = {
[0 ... sizeof(struct BlData_t) - 1] = 0xff
}
};
Затем вы можете получить доступ к своей структуре как data_with_ff.data
, определив указатель или макрос для удобства, если хотите.
(Читатели должны отметить, что ...
в назначенном инициализаторе — это расширение GCC; поскольку вопрос уже использовал эту функцию и помечен gcc, я предполагаю, что здесь все в порядке. Если используется компилятор, у которого его нет, я не знаю другого вариант, кроме .bytes = { 0xff, 0xff, 0xff ... }
с фактическим правильным количеством 0xff
s; вы, вероятно, захотите сгенерировать его с помощью скрипта.)
Всегда ли в C можно было инициализировать несколько элементов массива с помощью ...
? Я не думаю, что видел это раньше.
@ssbssa Нет, это никогда не было возможно. Это нестандартное расширение gcc (и ответ должен прояснить это, подтолкнуть-подтолкнуть).
@Lundin: подумал, что все в порядке, поскольку вопрос помечен gcc, но я все равно добавил упоминание.
@NateEldredge, как вы поняли, я намеренно пометил его gcc, чтобы указать, что да, я знаю, что использую расширения только для GCC.
Разумный способ сделать это — найти команду в скрипте компоновщика, говорящую ему отказаться от прикосновения к этой памяти в первую очередь. Потому что, почему вы хотите, чтобы он был стерт только для того, чтобы снова заполниться 0xFF? Это только приводит к ненужному износу вспышки ни за что.
Что-то в этом роде:
.bootloader_data (NOLOAD) :
{
. = ALIGN(512);
*(.bootloader_data *)
} >FLASH
Я даже не знал о (NOLOAD)
. Я собираюсь попробовать это. Если это сработает, то это абсолютно лучшее решение для моей ситуации и доказательство того, что я задал неправильный вопрос.
@Harvey С оговорками по синтаксису. Я так и не выучил все эти скрипты компоновщика (я работал примерно с десятью разными). Но обычно для компоновщиков есть инструкция "без загрузки"/"без инициализации".
Если вам действительно нужно выполнить эту инициализацию и в чистом стандартном C, вы можете обернуть свою внутреннюю структуру внутри анонимного объединения (C11), а затем инициализировать его с помощью макросов:
struct BlData_t {
uint8_t version[3];
uint8_t reserved;
uint8_t markers[128];
union {
struct AppData_t {
uint8_t version[3];
uint8_t reserved;
uint32_t crc;
} appInfo[512];
uint8_t raw [512];
};
};
#define INIT_VAL 0xFF, // note the comma
#define INIT_1 INIT_VAL
#define INIT_2 INIT_1 INIT_1
#define INIT_5 INIT_2 INIT_2 INIT_1
#define INIT_10 INIT_5 INIT_5
/* ... you get the idea */
#define INIT_512 INIT_500 INIT_10 INIT_2
const struct BlData_t bld = { .raw = {INIT_512} };
Этот метод также можно применять ко всей структуре, если вы, например, хотите инициализировать массив структур со всеми элементами, установленными на одни и те же значения во время компиляции.
За исключением того, что 512 в uint8_t raw [512];
должно больше походить на 6000 и IAC, код OP использует нестандартный packed
, и этот подход не зависит от заполнения, чтобы получить правильный размер.
@chux-ReinstateMonica Заполнение и инициализация - это две совершенно разные темы. Хотя на самом деле маловероятно, что эта структура получит какое-либо дополнение.
Я думаю, что наилучшее расположение вашего механизма зависит от варианта использования, который вы пытаетесь удовлетворить. Ваш код полагается на значения, установленные на 0xFF? Или это больше похоже на обнаружение неконтролируемого счетчика программ?