Миро Самек. Построение простых систем на ARM-контроллерах с использованием инструментов GNU (перевод)


Часть 1


1.1 Что нужно простому проекту на ARM

Необычайно популярные вычислительные ядра ARM7/ARM9 являются весьма сложными процессорами, поддерживающими два рабочих состояния: состояние ARM, в котором исполняются 32-битные выровненные по границе слов машинные инструкции набора ARM, и состояние THUMB, в котором исполняются 16-битные выровненные по границе полуслов машинные инструкции набора THUMB.

Кроме того, процессор имеет несколько уровней привелегий, как-то: USER, SYSTEM, SUPERVISOR, ABORT, UNDEFINED, IRQ и FIQ, различающихся режимом видимости регистров (регистровых банков) и возможностями исполнения привелигированных инструкций.

И наконец, вероятно, каждый ARM процессор позволяет переадресовывать вектора прерываний и содержит специфический для каждого производителя контроллер прерываний, допускающий вложенность прерываний.

К сожалению, реальным проектам требуются многие возможности вычислительного ядра и периферии. Ниже будут описаны наиболее типичные потребности простых проектов на ARM.


1.2 Поддержка переадресации векторов прерываний

Первые восемь слов (32 байта) памяти по адресу 0x00000000 содержат вектора исключений процессора ARM, причём, по адресу 0x00000000 расположен "Reset Vector". В момент начальной загрузки "Reset Vector" должен располагаться в программной памяти. Но большинство ARM контроллеров имеют возможность переадресации векторов в оперативную память, что позволяет изменять их в процессе работы.

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


1.3 Низкоуровневая инициализация на языке C/C++

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

Большинство указанных действий не требуют программирования на ассемблере и на самом деле гораздо проще решаются в C/C++, но при этом их надо завершить до вызова функции "main()". Последовательность действий при начальной загрузке, обсуждаемая в данной статье, позволяет проводить низкоуровневую инициализацию как на C/C++, так и на ассемблере.


1.4 Исполнение кода из оперативной памяти

Большинство контроллеров ARM начального уровня спроектированы с учётом исполнения кода из программной памяти (обычно NOR flash). Но программная память часто требует больше тактов ожидания, чем оперативная память и для некоторых моделей ARM контроллеров доступна только через 16-битную шину. Кроме того, исполнение кода из программной памяти требует больше мощности, чем исполнение его же из оперативной.

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


1.5 Совместное использование инструкций ARM и THUMB

В большинстве ARM контроллеров начального уровня 16-битный набор инструкций THUMB обеспечивает одновременно и большую компактность и большее быстродействие кода при исполнении из программной памяти, притом даже, что набор THUMB менее эффективен, чем 32-битный набор ARM. Статья показывает как увеличить производительность, комбинируя оба набора.


1.6 Раздельные секции стека

Наиболее распространённые командные файлы для компоновщика GNU при инициализации указателя стека без затей записывают произвольное значение в оперативную память. Стек обычно растёт в направлении динамической области памяти (heap) и определить моменты переполнения, если таковые случаются, довольно затруднительно.

В статье предлагается использовать под стек отдельную секцию, а для облегчения наблюдения за его использованием заполнять при инициализации специальной битовой комбинацией.

Преимуществом такого подхода является возможность обнаружения нехватки оперативной памяти не в процессе работы программы, а уже на этапе компоновки. Более того, отдельная секция стека позволяет располагать её в самой быстрой доступной памяти.


1.7 Отладочная и выходная конфигурации

Описываемый в статье "Makefile" позволяет использовать различные опции компилятора и компоновщика при сборке под различные конфигурации.


1.8 Поддержка C++

C++ требует дополнительного шага при инициализации для вызова статических конструкторов. GNU C++ создаёт несколько дополнительных секций для таблиц статических конструкторов и деструкторов.

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


1.9 Сокращение издержек C++

Если не соблюдать мер предосторожности и использовать стандартные настройки GNU g++, избыточный код C++ легко превысит 50KB, сделав использование этого языка невозможным для большинства ARM контроллеров начального уровня.

Тем не менее, ограничив возможности C++ до набора "Embedded C++" [4, 5], можно сделать издержки незначительными. Статья показывает как, используя инструменты GNU, уменьшить избыточность кода C++ до размера менее чем 300 дополнительных байт кода по сравнению с реализацией на чистом "Си".


1.10 Обработка исключений и прерываний ARM

Вычислительное ядро ARM поддерживает несколько исключений (Undefined Instruction, Prefetch Abort, Data Abort, Software Interrupt) и два вида прерываний: запрос прерывания (Interrupt Request - IRQ) и быстрый запрос прерывания (Fast Interrupt Request - FIQ).

При получении запроса на прерывание или исключение ядро ARM не сохраняет никаких регистров. Если в приложении требуются вложенные прерывания (позволяющие воспользоваться преимуществами приоритетного контроллера, наличествующего в большинстве процессоров ARM), то за сохранение регистров полностью отвечает программист.

Атрибут "__attribute__ ((interrupt ("IRQ")))" компилятора GNU не подходит для обработки вложенных прерываний, что вызывает необходимость использования ассемблера. Всё это приводит к заметному усложнению обработки прерываний.

Статья представляет надёжный способ обработки вложенных прерываний с использованием встроенного контроллера прерываний. Описываемый метод обеспечивает гораздо большую совместимость кода между традиционными ARMv4T и новыми ARMv7-M (Cortex) процессорами, нежели общепринятый подход.

ПредпросмотрAttachmentSize
bare_metal_arm_systems_html.zip327.73 КБ
blinky_files.zip173.94 КБ