#c #assembly #arm #startup #cortex-m3
#c #сборка #arm #запуск #cortex-m3
Вопрос:
Я изменяю файл запуска для микроконтроллера ARM Cortex-M3. Пока все работает нормально, но у меня есть вопрос относительно необходимости использования кода на ассемблере для выполнения заполнения нулем блока BSS.
По умолчанию прерывание сброса в файле запуска выглядит следующим образом:
// Zero fill the bss segment.
__asm( " ldr r0, =_bssn"
" ldr r1, =_ebssn"
" mov r2, #0n"
" .thumb_funcn"
" zero_loop:n"
" cmp r0, r1n"
" it ltn"
" strlt r2, [r0], #4n"
" blt zero_loop"
);
При использовании этого кода все работает так, как ожидалось. Однако, если я изменю предыдущий код на следующий, он перестанет работать:
// Zero fill the bss segment.
for(pui32Dest = amp;_bss; pui32Dest < amp;_ebss; )
{
*pui32Dest = 0;
}
В принципе, оба кода должны выполнять одно и то же (заполнять BSS нулями), но второй не работает по какой-то причине, которую я не могу понять. Я полагаю, что директива .thumb_func должна играть здесь определенную роль, но я не очень знаком с ассемблером ARM. Есть идеи или указания, которые помогут мне понять? Спасибо!
Редактировать: Кстати, код для инициализации сегмента данных (например, копирование с флэш-памяти в оперативную память) выглядит следующим образом и работает просто отлично.
// Copy the data segment initializers from flash to SRAM.
pui32Src = amp;_etext;
for(pui32Dest = amp;_data; pui32Dest < amp;_edata; )
{
*pui32Dest = *pui32Src ;
}
Редактировать: Добавлен разрозненный код для обеих функций.
Сборка для первого выглядит как:
2003bc: 4806 ldr r0, [pc, #24] ; (2003d8 <zero_loop 0x14>)
2003be: 4907 ldr r1, [pc, #28] ; (2003dc <zero_loop 0x18>)
2003c0: f04f 0200 mov.w r2, #0
002003c4 <zero_loop>:
2003c4: 4288 cmp r0, r1
2003c6: bfb8 it lt
2003c8: f840 2b04 strlt.w r2, [r0], #4
2003cc: dbfa blt.n 2003c4 <zero_loop>
Сборка для второго выглядит следующим образом:
2003bc: f645 5318 movw r3, #23832 ; 0x5d18
2003c0: f2c2 0300 movt r3, #8192 ; 0x2000
2003c4: 9300 str r3, [sp, #0]
2003c6: e004 b.n 2003d2 <ResetISR 0x6e>
2003c8: 9b00 ldr r3, [sp, #0]
2003ca: 1d1a adds r2, r3, #4
2003cc: 9200 str r2, [sp, #0]
2003ce: 2200 movs r2, #0
2003d0: 601a str r2, [r3, #0]
2003d2: 9a00 ldr r2, [sp, #0]
2003d4: f644 033c movw r3, #18492 ; 0x483c
2003d8: f2c2 0300 movt r3, #8192 ; 0x2000
2003dc: 429a cmp r2, r3
2003de: d3f3 bcc.n 2003c8 <ResetISR 0x64>
Комментарии:
1. В каком смысле это «перестает работать»?
2. Он зависает, выполнение не достигает основной функции. Для меня это может быть либо что-то связанное с ARM vs. Режим Thumb или что цикл неправильный (например, он не заканчивается).
3. обычно вы не хотите использовать C, пока не закончите загрузку C, курицу и яйцо, проблемы, подобные этой, ожидаемы. настройте стек, очистите файл .bss и скопируйте . данные в asm (в идеале не встроенные) как минимум до того, как будет затронута первая буква C…
4. Код запуска из codered / LPCXpresso / precision32 использует c / c и выглядит очень похоже на то, что вы делаете. Нет причин, по которым это не может или не должно быть на c.
Ответ №1:
Если исходный стек находится в .bss
разделе, как предложено, вы можете видеть из дизассемблирования, почему код C завершается ошибкой — он загружает текущий указатель из стека, сохраняет увеличенный указатель обратно в стек, обнуляет местоположение, затем перезагружает увеличенный указатель для следующей итерации. Если вы обнуляете содержимое стека во время их использования, происходят плохие вещи.
В этом случае включение оптимизации может исправить это (интеллектуальный компилятор должен генерировать практически то же самое, что и ассемблерный код, если он действительно пытается). В более общем плане, однако, вероятно, безопаснее придерживаться ассемблерного кода при выполнении подобных действий, которые обычно выполняются на уровне ниже среды выполнения C — загрузка среды C из кода C, который ожидает, что эта среда уже существует, в лучшем случае рискованна, поскольку вы можете только надеяться, что код не попытается использовать что-либо, что еще не настроено.
После быстрого ознакомления (я не слишком знаком со спецификой разработки Cortex-M), кажется, альтернативным / дополнительным решением может быть настройка сценария компоновщика для перемещения стека куда-то еще.
Комментарии:
1. почему stack должен быть в bss?
2. @escrafford Я не вижу никакой веской причины, но кто-то предположил, что это было в ныне удаленном ответе, и в моем кратком поиске я нашел пример скрипта компоновщика, который это сделал, так что, я думаю, есть по крайней мере один набор инструментов, который это делает. Конечно, есть и другие возможности, такие как оставшийся загрузчик,
sp
который просто случайно совпадает с приложением.bss
, или что-то еще (при повторном просмотре эти адреса в дизассемблировании C выглядят немного сомнительными)