#c #stm32 #gnu #ram #linker-scripts
#c #stm32 #гну #ОЗУ #компоновщик-скрипты
Вопрос:
Я работаю с микроконтроллером STM32H7 и GNU / GCC, в моем коде я использую только ОЗУ DTCM, но я хочу сохранить некоторые буферы в другой памяти, доступной с помощью DMA.
Я совершенно новичок в скрипте компоновщика, нужно ли мне редактировать код запуска?
Вот мой скрипт компоновщика, я добавил немного кода в SECTIONS
/* Memories definition */
MEMORY
{
DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K
RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 512K
RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K
RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K
}
/* Sections */
SECTIONS
{
...
.ram1block 0x24000000 :
{
KEEP(*(.ram1section))
} > RAM_D1
...
}
Я хочу использовать этот атрибут :
uint8_t __attribute__(( section(".ram1section") )) ads_buf[READBACK_LENGTH];
Правильно ли я все делаю ?
У вас есть какие — нибудь темы или советы ? Я новичок в этих сценариях и немного растерян
РЕДАКТИРОВАТЬ: По-видимому, есть более простое решение:
в файле компоновщика
SECTIONS
{
ads_buf=0x24000000;
...
}
В источнике
extern uint8_t ads_buf[READBACK_LENGTH];
Кто-нибудь может подтвердить, что это решение действительно?
Ответ №1:
Ваше «более простое» решение означает, что вам придется назначать адреса для всех объектов в этом разделе памяти вручную, проявляя особую осторожность, чтобы избежать дублирования. Это именно то, чего компоновщики были изобретены, чтобы избежать, поэтому я бы посоветовал не идти этим путем.
Вам не нужно снова указывать адрес для раздела вывода, только для диапазона памяти. Нужно ли вам изменять код запуска, зависит от того, нужно ли инициализировать этот раздел при запуске.
Нет инициализации
Если вам не нужна инициализация, потому что, например, вы будете заполнять буфер входящими данными перед его чтением, измените сценарий компоновщика следующим образом:
.ram1block (NOLOAD) :
{
KEEP(*(.ram1section))
} > RAM_D1
И все готово.
Тривиальная инициализация
Если вам нужно, чтобы он был инициализирован тривиально, например, все нули, добавьте адресные символы следующим образом:
.ram1blockBss (NOLOAD) :
{
. = ALIGN(4);
_BeginRam1Bss = .;
KEEP(*(.ram1sectionBss))
. = ALIGN(4);
_EndRam1Bss = .;
} > RAM_D1
Выравнивание адреса упрощает и ускоряет инициализацию. Обратите внимание, что я переименовал раздел ввода ram1sectionBss
в, чтобы указать, что этот раздел инициализирован нулем. Добавьте что-то вроде этого в запуск -код для нулевой инициализации памяти:
ldr r0, =_BeginRam1Bss
ldr r1, =_EndRam1Bss
ldr r2, =0
b 2f
1: str r2, [r0], #4
2: cmp r0, r1
blo 1b
Это эффективно инициализирует весь блок нулями, так что переменные C, определенные как
uint8_t __attribute__(( section(".ram1sectionBss") )) ads_buf[READBACK_LENGTH];
будет инициализирован нулем.
Инициализация значения
Если вам нужно инициализировать содержимое в вашей памяти с различными значениями, измените определение раздела компоновщика следующим образом:
.ram1blockData (NOLOAD) :
{
. = ALIGN(4);
_BeginRam1Data = .;
KEEP(*(.ram1sectionData))
. = ALIGN(4);
_EndRam1Data = .;
} > RAM_D1 AT> FLASH
_InitRam1Data = LOADADDR (.ram1blockData);
Также добавьте a . = ALIGN(4);
в последний раздел перед .ram1blockData
тем, как он войдет FLASH
, чтобы убедиться, что адрес загрузки во флэш-памяти также выровнен.
Затем добавьте следующее в код запуска:
ldr r0, =_BeginRam1Data
ldr r1, =_EndRam1Data
ldr r2, =_InitRam1Data
b 2f
1: ldr r3, [r2], #4
str r3, [r0], #4
2: cmp r0, r1
blo 1b
Если затем вы определите свою переменную C как
uint8_t __attribute__(( section(".ram1sectionData") )) ads_buf[READBACK_LENGTH] = { 1, 2, 3, 4 };
он будет правильно инициализирован. Еще раз обратите внимание на переименованный раздел ( ram1sectionData
для обозначения инициализации данных).
Тривиальная инициализация значение
Если вам нужны как инициализированные нулем, так и инициализированные значением данные в вашем блоке памяти, просто поместите оба определения разделов в сценарий компоновщика и оба блока сборки в коде запуска, чтобы сделать определения C, подобные этой работе:
uint8_t __attribute__(( section(".ram1sectionBss") )) ads_buf1[READBACK_LENGTH];
uint8_t __attribute__(( section(".ram1sectionData") )) ads_buf2[READBACK_LENGTH] = { 1, 2, 3, 4 };
PS: Обратите _BeginRam1Bss
внимание, что подобные идентификаторы начинаются с подчеркивания заглавной буквы, которые являются зарезервированными идентификаторами C. Это означает, что вы не можете случайно использовать их в коде на языке Си, что привело бы к столкновению со сценарием компоновщика. Компоновщик-скрипт Startup-Code являются частью реализации и должны обеспечивать соответствующую среду выполнения для обычного кода C, не «занимая» не зарезервированные идентификаторы, которые должны использоваться кодом C.
Комментарии:
1. С удовольствием 🙂 Также не то, что вам может потребоваться включить этот конкретный блок ОЗУ в коде запуска перед его инициализацией, поскольку некоторые (не все) блоки ОЗУ STM32 по умолчанию отключены при включении питания.
Ответ №2:
Нет, это недопустимое решение. Почему? — потому что он объявляет только символ. Компоновщик не знает, что в памяти по этому адресу что-то есть. Он может разместить там другие данные. Очень, очень плохая идея.