Понимание сценариев компоновщика для STM32F103C8T6 - PullRequest
1 голос
/ 04 февраля 2020

Я недавно вошел в программирование с нуля STM32F103C8T6, и реализация сценария компоновщика кажется немного запутанной. Я нашел две версии скриптов компоновщика в сети, и удивительно, что обе они работают, как и ожидалось, несмотря на огромную разницу в их содержании.

Версия 1 , используется здесь

SECTIONS
{
    .  = 0x0;         /* From 0x00000000 */
    .text : 
    {
        *(isr_vector) /* Interrupt Service Routine Vector table */
        *(.text)      /* Program code */
    }
}

и полученное в результате встроенное программное обеспечение мигает с flash write_bank 0 add.bin 0

Версия 2 , как используется здесь

MEMORY {
    FLASH (rw): ORIGIN = 0x8000000, LENGTH = 64K
    RAM (rwx): ORIGIN = 0x20000000, LENGTH = 20K
}

ENTRY(Reset_Handler)
SECTIONS
{   .text : {
        KEEP(* (.isr_vector))
        * (.text*)
    } > FLASH

    __StackTop = ORIGIN(RAM) + LENGTH(RAM);
}

и результирующая прошивка мигает с flash write_image erase main.bin 0x8000000

Как вы можете видеть, в скрипте компоновщика и в команде OpenOCD для прошивки sh прошивки, версия 1 мигает .text до 0x00000000, тогда как версия 2 мигает до 0x8000000. Во-первых, я не уверен, что означают эти адреса: это LMA или VMA? Во-вторых, почему перепрошивка на разные адреса имеет одинаковый эффект?

Я провел некоторое исследование, но руководство по программированию , похоже, не решает мою путаницу.

1 Ответ

2 голосов
/ 05 февраля 2020

Хорошо, так что, может быть, это то, что я могу ответить, не понял ваш вопрос в комментарии другого вопроса.

Процессор загружается, ища векторную таблицу по адресу 0x00000000 в адресном пространстве оружия. ST реализовал свою часть так, что приложение fla sh находится по адресу 0x08000000 в адресном пространстве оружия.

В зависимости от режима загрузки ST может, если вы будете зеркалировать либо встроенную программу начального загрузчика на адрес 0x00000000, либо приложение. Таким образом, доступ к руке по адресу 0x00000000 будет возвращаться со значениями, найденными в fla sh Если зеркалировать приложение, то и 0x00000000, и 0x08000000 будут считывать значения с одного и того же физического устройства fla sh и возвращать их. Здесь нет magi c, шина ARM имеет адрес и объем данных, которые она хочет прочитать, logi c имеет маски и совпадения, чтобы определить, какое это адресное пространство, то если от 0x00000000 до некоторого количества Кбайт, тогда, если ремешок читается одним способом из одного флага sh, другой считывается из другого. Аналогично с write.

Поиск BOOT0 в справочном руководстве для детали.

В идеале вы хотите связать для 0x08000000 (или 0x00200000 для некоторых деталей, но не для всех), чтобы вектор записи таблицы, прочитанные по адресу 0x00000004, 0x00000008 и т. д., возвращают адреса 0x0800xxxx, таким образом, только таблица векторов фактически считывается из 0x00000000, а затем остальная часть программы в адресном пространстве приложения. В документации вы увидите, что адресное пространство 0x00000000 для деталей с большим количеством fla sh не поддерживает весь размер fla sh, поэтому вы не можете использовать все fla sh, если вы ссылаетесь на 0x00000000

теперь эти сценарии компоновщика интересны, прежде всего, если вы видите это в таблице векторов для cortex-m

.word       _start + 1
.word       _nmi_handler + 1
.word       _hard_fault + 1

, найдите другой пример.

.thumb

.section isr_vector
.word 0x20001000
.word one
.word two

.text
.thumb_func
one:
    b one
.thumb_func
two:
    b two
.thumb_func

сначала скрипт компоновщика

Disassembly of section .text:

00000000 <one-0xc>:
   0:   20001000    andcs   r1, r0, r0
   4:   0000000d    andeq   r0, r0, sp
   8:   0000000f    andeq   r0, r0, pc

0000000c <one>:
   c:   e7fe        b.n c <one>

0000000e <two>:
   e:   e7fe        b.n e <two>

в этом случае, когда рычаг выполняет чтение таблицы векторов, чтобы найти вектор сброса по адресу 0x00000004, часть ST вернет значение во втором слове приложения fla sh (думаю, адрес 0x08000004)

, в этом случае он обнаруживает 0000000d, что означает начало выборки инструкций по адресу 0x0000000 c.

со вторым сценарием компоновщика

.thumb

.section .isr_vector
.globl _isr_vector
_isr_vector:
.word 0x20001000
.word Reset_Handler

.text
.globl Reset_Handler
.thumb_func
Reset_Handler:
    b .


Disassembly of section .text:

08000000 <_isr_vector>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000009    stmdaeq r0, {r0, r3}

08000008 <Reset_Handler>:
 8000008:   e7fe        b.n 8000008 <Reset_Handler>

в в этом случае, когда рука ищет вектор сброса в 0x00000004, он получит 0x08000009, что означает выбор первых инструкций по адресу 0x08000008, который находится в адресном пространстве для пар t для приложения fla sh, это предпочтительно.

У некоторых деталей ST есть небольшое окно с размером 0x00200000, которое может читать некоторые из этих флагов sh быстрее (это дело рук ITCM против AXIM, читайте документы cortex-m7).

По msp432, и я думаю, что fla приложения sh - это 0x01000000, то же самое, что и зеркалирование, просто другой адрес.

.thumb_func
.align  2
.global Reset_Handler
.type   Reset_Handler, %function
Reset_Handler:

Обратите внимание, что во втором примере у автора есть дополнительный код, оба .thumb_fun c и .type помечают эту метку как функцию (которая делает ORR с 1, чтобы установить lsbit адреса, чтобы вам не нужно было иметь уродливую векторную таблицу, используйте инструменты)

Например:

.thumb

.section .isr_vector
.globl _isr_vector
_isr_vector:
.word 0x20001000
.word Reset_Handler
.word Something_Else

.text
.globl Reset_Handler
.thumb_func
Reset_Handler:
    b .

.type Something_Else, %function
Something_Else:
    b .


Disassembly of section .text:

08000000 <_isr_vector>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   0800000d    stmdaeq r0, {r0, r2, r3}
 8000008:   0800000f    stmdaeq r0, {r0, r1, r2, r3}

0800000c <Reset_Handler>:
 800000c:   e7fe        b.n 800000c <Reset_Handler>

0800000e <Something_Else>:
 800000e:   e7fe        b.n 800000e <Something_Else>

оба сработали, какой вы используете, это ваше личное предпочтение

.thumb_func

чисто и просто и не требует соответствия метке, но это зависит от позиции, следующая метка является той это помечено как функция. Также нет .arm_fun c

.type labelname, %function

работает как для кода руки, так и для большого пальца, это, возможно, немного больше печатать, и вы должны соответствовать названию этикетки, но есть плюсы в этом, как вы ясно обозначают метку, которую вы хотите идентифицировать как адрес функции, и эта привычка работает как в режиме «рука», так и в режиме «большой палец».

Оба автора (или это один и тот же человек дважды?) создали ненужную работу.

Рассмотрим эти

so.s

.thumb
.word 0x20001000
.word one
.word two

.thumb_func
one:
    b .

.thumb_func
two:
    b .

next.s

add r1,r2,r3
add r2,r3,r4
add r3,r4,r5

so.ld

MEMORY
{
    xyz : ORIGIN = 0x08000000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > xyz
}

arm-none-eabi-as so.s -o so.o
arm-none-eabi-as next.s -o next.o
arm-none-eabi-ld -T so.ld so.o next.o -o so.elf
arm-none-eabi-objdump -D so.elf

so.elf:     file format elf32-littlearm


Disassembly of section .text:

08000000 <one-0xc>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   0800000d    stmdaeq r0, {r0, r2, r3}
 8000008:   0800000f    stmdaeq r0, {r0, r1, r2, r3}

0800000c <one>:
 800000c:   e7fe        b.n 800000c <one>

0800000e <two>:
 800000e:   e7fe        b.n 800000e <two>
 8000010:   e0821003    add r1, r2, r3
 8000014:   e0832004    add r2, r3, r4
 8000018:   e0843005    add r3, r4, r5

это хорошо

но если вы

arm-none-eabi-ld -T so.ld next.o so.o -o so.elf
arm-none-eabi-objdump -D so.elf

so.elf:     file format elf32-littlearm


Disassembly of section .text:

08000000 <one-0x18>:
 8000000:   e0821003    add r1, r2, r3
 8000004:   e0832004    add r2, r3, r4
 8000008:   e0843005    add r3, r4, r5
 800000c:   20001000    andcs   r1, r0, r0
 8000010:   08000019    stmdaeq r0, {r0, r3, r4}
 8000014:   0800001b    stmdaeq r0, {r0, r1, r3, r4}

08000018 <one>:
 8000018:   e7fe        b.n 8000018 <one>

0800001a <two>:
 800001a:   e7fe        b.n 800001a <two>

это плохо.

если не вызывается в сценарии компоновщика, то позиция ссылки в командной строке определяет порядок элементов .text в файле. При предоставлении примера или создании проекта у вас не должно быть make-файла или инструкций по сборке для go с кодом? Вы действительно должны выполнить дополнительную работу в скрипте компоновщика? YMMV

Также обратите внимание, что метки памяти в скрипте компоновщика являются просто метками для соединения точек между MEMORY и SECTIONS. Вы можете использовать любые строки, которые вы хотите, в определенных пределах и с некоторыми исключениями.

Я использовал (rw), но это стало проблематично c пришлось перестроить мой скрипт компоновщика между двумя версиями бинутилс. (мне кажется, что в этом примере есть ошибка) Если компоновщик жалуется на отсутствующий раздел (.rodata), просто добавьте его, ничего не значит.

Красота чистого металла в том, что вы можете выбирать как вы хотите это сделать, если вы не используете библиотеки, то вы должны играть в их песочнице. И часто вы найдете более сложные скриптовые компоновщики и бутстрапы, которые пытаются охватить все возможные варианты использования и функции. Просто создайте тот, который соответствует вашему варианту использования.

Вы можете написать свой код так, чтобы вам не приходилось обнулять .bss или копировать .data, или вы можете выбрать поддержку .data, но не .bss, или вы можете полностью поддерживает как относительно простой скрипт компоновщика, так и bootstrap. У вас есть место на этом mcu, вам действительно нужно спроектировать стек в скрипт компоновщика? Вы знаете, насколько велика эта часть, просто сделайте стек сверху вниз. (если вам нужно набрать размер только один раз, если вы сделаете компоновщик волхвов c) На ваш выбор. Если по какой-то причине вы хотите поддерживать кучу (почему у вас когда-нибудь будет куча на mcu? Не очень хорошая вещь), вы можете добавить это и в скрипт компоновщика.

Опять красота голого металла, если он работает, вы можете делать все, что захотите. Я рекомендую выйти за рамки того, что сделали эти люди, и немного лучше изучить инструменты (в первую очередь на языке ассемблера), чтобы у вас было больше выбора относительно того, как вы это делаете.

Почему обе работы заключаются в том, что часть зеркально отображает часть / все адресное пространство 0x08000000 в 0x00000000. поэтому связь с любым из них будет работать в пределах окна.

Вы можете выбрать, что пометить этим, я использую термин адресное пространство руки, я бы также использовал термин физический адрес, немногие, если какое-либо из этих ядер имеет пространство виртуальной памяти , так что это не имеет смысла. Шина ARM от ARM IP (рука не производит микросхемы, она заставляет ядра, которые такие компании, как ST, покупать и выкладывать свои собственные или другие логики c, чтобы сделать чип), будет отправлять чтение с одним из этих физических адресов на То, как реагирует чип, зависит от того, как они его разработали. Записи в таблице векторов находятся по известным адресам, что означает, что logi c будет использовать известный адрес для чтения каждого элемента, а чип и программист должны ответить, если существует MMU, который может изменить адресное пространство, вектор таблица по-прежнему должна находиться по одному из этих физических адресов, если бы обработчик logi c проходил через mmu.

Поэтому, учитывая тест с множественным выбором, я бы сказал, логический адрес памяти, LMA.

Следующая вещь здесь - openocd поддерживает запись в 0x00000000. openocd, поддерживающий fla sh пишет вообще, является бонусом, так как это спецификация чипа c, и кто-то должен положить время. Из-за природы 0x00000000 для этих частей и того, как вы включили его (контакты-ремешки), вы можете неправильно связать его, и / или в реализации openocd есть предположение или определение, которое всегда превращает 0x00000000 в 0x08000000 или что-то подобное, может находиться в самом файле .cfg, который я недавно просматривал для вас, но не искал эту деталь.

...