Перейти к содержимому

Arm thumb что это

  • автор:

ARM аccемблер

Привет всем!
По роду деятельности я программист на Java. Последние месяцы работы заставили меня познакомиться с разработкой под Android NDK и соответственно написание нативных приложений на С. Тут я столкнулся с проблемой оптимизации Linux библиотек. Многие оказались абсолютно не оптимизированы под ARM и сильно нагружали процессор. Ранее я практически не программировал на ассемблере, поэтому сначала было сложно начать изучать этот язык, но все же я решил попробовать. Эта статья написана, так сказать, от новичка для новичков. Я постараюсь описать те основы, которые уже изучил, надеюсь кого-то это заинтересует. Кроме того, буду рад конструктивной критике со стороны профессионалов.

Введение

Итак, для начала разберёмся что же такое ARM. Википедия дает такое определение:

Архитектура ARM (Advanced RISC Machine, Acorn RISC Machine, усовершенствованная RISC-машина) — семейство лицензируемых 32-битных и 64-битных микропроцессорных ядер разработки компании ARM Limited. Компания занимается исключительно разработкой ядер и инструментов для них (компиляторы, средства отладки и т. п.), зарабатывая на лицензировании архитектуры сторонним производителям.

Если кто не знает, сейчас большая часть мобильных устройств, планшетов разработаны именно на этой архитектуре процессоров. Основным преимуществом данного семейства является низкое энергопотребление, благодаря чему он часто используется в различных встроенных системах. Архитектура развивалась с течением времени, и начиная с ARMv7 были определены 3 профиля: ‘A’(application) — приложения, ‘R’(real time) — в реальном времени,’M’(microcontroller) — микроконтроллер. Историю разработки этой технологии и другие интересный данные вы можете прочитать в Википедии или погуглив в интернете. ARM поддерживает разные режимы работы (Thumb и ARM, кроме того в последние время появился Thumb-2, являющийся смесью ARM и Thumb). В данной статье рассмотрим собственно режим ARM, в котором исполняется 32-битный набор команд.

  • 37 регистров (из которых видимых при разработке только 17)
  • Арифметико-логи́ческое устройство (АЛУ) — выполняет арифметические и логические задачи
  • Barrel shifter — устройство, созданное для перемещения блоков данных на определенное количество бит
  • The CP15 — специальная система, контроллирующая ARM сопроцессоры
  • Декодер инструкций — занимается преобразованием инструкции в последовательность микроопераций
Конвейерное исполнение (Pipeline execution)

В ARM процессорах используется 3-стадийный конвейер (начиная с ARM8 был реализова 5-стадийный конвейер). Рассмотрим простой конвейер на примере процессора ARM7TDMI. Исполнение каждой инструкции состоит из трёх ступеней:

1. Этап выборки (F)
На этом этапе инструкции поступают из ОЗУ в конвейер процессора.
2. Этап декодирования (D)
Инструкции декодируются и распознаётся их тип.
3. Этап исполнения (E)
Данные поступают в ALU и исполняются и полученное значение записывается в заданный регистр.

Но при разработке надо учитывать, что, есть инструкции, которые используют несколько циклов исполнения, например, load(LDR) или store. В таком случае этап исполнения (E) разделяется на этапы (E1, E2, E3. ).

Условное выполнение

Одна из важнейших функций ARM ассемблера — условное выполнение. Каждая инструкция может исполняться условно и для этого используются суффиксы. Если суффикс добавляется к названию инструкции, то прежде чем выполнить ее, происходит проверка параметров. Если параметры не соответствуют условию, то инструкция не выполняется. Суффиксы:
MI — отрицательное число
PL — положительное или ноль
AL — выполнять инструкцию всегда
Суффиксов условного выполнения намного больше. Остальные суффиксы и примеры прочитать в официальной документации: ARM документация
А теперь пришло время рассмотреть…

Основы синтаксиса ARM ассемблера

Тем, кто раньше работал с ассемблером этот пункт можно фактически пропустить. Для всех остальных опишу основы работы с этим языком. Итак, каждая программа на ассемблере состоит из инструкций. Инструкция создаётся таким образом:
<инструкция|операнды>
Метка — необязательный параметр. Инструкция — непосредственно мнемоника инструкции процессору. Основные инструкции и их использование будет разобрано далее. Операнды — константы, адреса регистров, адреса в оперативной памяти. Комментарий — необязательный параметр, который не влияет на исполнение программы.

Имена регистров

Разрешены следующие имена регистров:
1.r0-r15

3.v1-v8 (переменные регистры, с r4 по r11)

4.sb and SB (статический регистр, r9)

Аппаратные аспекты

И ARM7 и ARM7T ядра в одном тактовом цикле, используют 3-уровневый конвейер с фазами выборки, декодирования и выполнения. Поток команд через каждый уровень конвейера управляется высокими и низкими фазами тактового сигнала. В ядре ARM7TDMI неиспользуемая фаза тактового сигнала используется для декомпрессирования команд Thumb в каскаде декодирования. Следовательно, в одном тактовом цикле производится и декодирование и выполнение команды, не требуется никаких дополнительных непроизводительных затрат синхронизации.

Рисунок 11: Thumb декодирование и декомпрессия

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

В Thumb состоянии мультиплексоры направляют Thumb команды через логику декомпрессии Thumb, разворачивающую команду Thumb в ее эквивалент ARM команды . Затем команда ARM выполняется в нормальном режиме. Это легко понять на примере:

Рисунок 12: Трансляция команды ADD Thumb в команду ADD ARM

Изящное решение

Как видно из рисунка, старший опкод команды Thumb помещается в команду ARM, а младший транслируется через справочную таблицу.

В команде ARM всегда присутствует код условия, определяемый из старшим операционным кодом.

Старший опкод определяет маршрут операнда от операционного кода Thumb до операционного кода ARM. Определители регистров расширяются дополнительным нулем от опкода Thumb (3 бита) до 4 битов, поскольку эта команда Thumb обращается только к регистрам ARM R0-R7. Значение константы также расширяется нулями, определяя 8-разрядную константу в опкоде ARM.

Это изящное решение применимо на любом ядре ARM и будет использоваться в перспективных ARM архитектурах.

Концепция Thumb

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

Рисунок 1: Команды Thumb как кодированное подмножество системы команд ARM

Уникальное преимущество

Технология Thumb — это не только смешанная система команд. Thumb-ориентированные ядра имеют две отдельных системы команд — уникальное достоинство, позволяющее разработчику использовать всю мощность 32-разрядной системы команд ARM при использовании преимуществ малого размера кода системы команд Thumb. Тот факт, что две системы команд являются совершенно отдельными, говорит о том, что средства декодирующей логики также чрезвычайно просты, что в свою очередь, сохраняет малым размер кристалла и сохраняет лучшее в отрасли соотношение производительность/потребление.

Размер и критичные к производительности подпрограммы

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

32-разрядная RISC производительность

Thumb -ориентированные ядра типа ARM7TDMI имеют полную 32-разрядную архитектуру ARM, так что разработчик сохраняет 32-разрядную производительность RISC архитектуры. Комбинация двух систем команд, выполняющихся на 32-разрядном Thumb -ориентированном ядре, обеспечивает эффективное решение проблемы больших размеров кода и проблемы невысокой производительности 16-разрядных систем.

Улучшение плотности кода на 30 %

Полученные к настоящему времени результаты показали улучшение плотности кода на 30%, по сравнению с кодом ARM, что позволяет считать Thumb -ориентированные процессоры лучшими по плотности кода в сравнении и с традиционными CISC процессорами.

Рисунок 2: Ядро ARM7TDMI и расположение декомпрессора команд Thumb

Поддержка полуслов

Кроме введения новых Thumb команд, фирма ARM добавила к системам команд и ARM и Thumb поддержку формата полуслов (16-разрядных данных). Следовательно архитектура ARM теперь полностью поддерживает 8, 16 и 32-разрядные данные. Были добавлены и для Thumb и ARM ядер операции со знаками для поддержки ими 8 и 16-разрядных операций с данными со знаками.

Расширение возможностей комплекта средств разработки программного обеспечения ARM

омплект средств разработки программного обеспечения ARM также был расширен, чтобы обеспечить поддержку разработки кодов Thumb. Комплект позволяет программисту писать и размещать в памяти системы коды ARM, коды Thumb или оба вместе.

Рисунок 3: Последовательность этапов проектирования программного обеспечения для Thumb -ориентированного ядра

Arm thumb что это

Ниже представлены 10 особенностей ARM-архитектуры, которые, по-моему мнению, являются интересными и заслуживающими внимания. Люди, занимающиеся программированием под ARM (в особенности с использованием ассемблера) врядли найдут здесь что-то новое, однако новички и люди, знакомые с прочими архитектурами (AVR, MIPS, x86 и прочими) могут найти интересные вещи.

Все написанное применимо для ARMv4T (ARM7TDMI-S), однако, некоторые особенности встречаются и в ARMv7 (Cortex-Mx).

    Любая инструкция допускает условное выполнение. Специально для этого 4 бита отведены под флаги условного выполнения. Это хорошо видно в таблице, представленной в официальной документации: Это позволяет сооружать такие хитрые конструкции:

; if (r0 == 0) ; r0 = 2; ; else ; r0 = 1; cmp r0, #0 moveq r0, #2 movne r0, #1
------------------------------------------------------ ; Данная конструкция замечена за GCC: mov r0, #0x12, lsl #24 or r0, r0, #0x34, lsl #16 or r0, r0, #0x56, lsl #8 or r0, r0, #0x78 ------------------------------------------------------ ; Но более распространен вариант: . ldr r0, [pc+#const1_offset] . const1: DD 0x12345678 ------------------------------------------------------
------------------------------------------------------ ; Сохраняем регистры, требуемые в функции. Так же сохраняем адрес ; возврата из функции, это позволяет осуществлять вложенные вызовы. stm sp, ; Восстановить используемые регистры при выходе из функции, ; при этом LR восстанавливается в PC, порождая тем самым ; возврат из функции. ldm sp, ------------------------------------------------------
------------------------------------------------------ unsigned char data[5] = < 0x11, 0x22, 0x33, 0x44, 0x55 >; unsigned long tmp = *(unsigned long*)&data[1]; printf("tmp = 0x%08x;rn", tmp); ------------------------------------------------------ tmp = 0x11443322; /* Вместо ожидаемого 0x55443322 */ ------------------------------------------------------

Для получения корректного результата в Keil Realview MDK достаточно добавить макрос __packed:

------------------------------------------------------ unsigned char data[5] = < 0x11, 0x22, 0x33, 0x44, 0x55 >; unsigned long tmp = *(__packed unsigned long*)&data[1]; printf("tmp = 0x%08x;rn", tmp); ------------------------------------------------------ tmp = 0x55443322; ------------------------------------------------------

  • Для каждого режима работы процессора выделен свой стек. Мало того, во всех режимах банк регистров частично заменяется (а в режиме FIQ и вовсе заменяется половина всех регистров). В таблице ниже представлены регистры для каждого режима и помечены заменяемые регистры. Сделано это для ускорения входа-выхода из прерываний, благодаря этому требуется сохраненять и восстанавливать меньше данных.
  • Для вызова функций, возврата из функций и осуществления прыжков используется один и тот же класс инструкций.

    ------------------------------------------------------ . ; Вызов 1 bl func1 ; Вызов 2 ; Обычно используется по вызову функции по указателю или ; просто очень дальнему вызову, когда BL "не дотягивается". ldr r0, =func1 mov lr, pc bx r0 ; Вызов 3 (возможен на ARM9): ldr r0, =func1 blx r0 . func1: ; Тело функции bx lr ------------------------------------------------------
    ------------------------------------------------------ func(0x01, 0x02); ------------------------------------------------------ mov r0, #0x01 mov r1, #0x02 bl func ------------------------------------------------------

    О сайте

    Подборка статей и отчетов о различных математических и электронных экспериментах.

  • Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *