Файл запуска ARM Cortex-M3

#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 выглядят немного сомнительными)