Подтвердить что ты не робот

Различия между архитектурами ARM с точки зрения программиста C?

Я новичок в программировании для ARM. Я заметил, что существует несколько архитектур, таких как ARMv4, ARMv5, ARMv6 и т.д. В чем разница между ними? Есть ли у них разные наборы инструкций или поведения?

Самое главное, если я скомпилирую код C для ARMv6, будет ли он работать на ARMv5? Что относительно кода ARMv5 работает на ARMv6? Или мне нужно только беспокоиться о различии, если бы я писал код сборки ядра?

4b9b3361

Ответ 1

Мир ARM немного запутан.

Для программистов C все просто: все архитектуры ARM предлагают стандартную 32-битную модель с плоской адресацией. До тех пор, пока вы остаетесь с исходным кодом C, единственное различие, которое вы можете увидеть, - это утверждение и эффективность. Большинство процессоров ARM (даже старых моделей) могут быть как большими, так и малоподобными; выбор затем осуществляется с помощью логической платы и операционной системы. Хороший код C абсолютно нейтрален: он компилируется и работает правильно, независимо от конечной точки платформы (конечный нейтралитет хорош для надежности и ремонтопригодности, но также и для производительности: не нейтральный код - это код, который обращается к тем же данным через указатели разного размера, и это приводит к хаосу со строгими правилами псевдонимов, которые компилятор использует для оптимизации кода).

Ситуация совсем другая, если вы считаете совместимость двоичной (т.е. повторное использование кода, который был скомпилирован один раз):


  • Существует несколько наборов инструкций:
    • исходный набор команд ARM с 26-битным счетчиком программ (очень старый, вряд ли встречается в настоящее время)
    • набор команд ARM с 32-разрядным программным счетчиком (часто называемым "ARM-кодом" )
    • набор инструкций Thumb (16-битные упрощенные коды операций)
    • Набор команд Thumb-2 (Thumb с расширениями)

Данный процессор может реализовывать несколько наборов команд. Новейшим процессором, который знает только ARM-код, является StrongARM, представитель ARMv4, который уже довольно старый (15 лет). ARM7TDMI (архитектура ARMv4T) знает как ARM, так и Thumb, как и почти все последующие ARM-системы, кроме Cortex-M. Код ARM и Thumb можно смешивать вместе в одном и том же приложении, при условии, что соответствующий клей будет вставлен в случае изменений конвенций; это называется перехватом большого пальца и может автоматически обрабатываться компилятором C.

Cortex-M0 знает только инструкции Thumb. Он знает несколько расширений, потому что в "обычных" ARM-процессорах операционная система должна использовать ARM-код (для обработки прерываний); таким образом, Cortex-M0 знает несколько вещей Thumb-for-OS. Это не имеет значения для кода приложения.

Другой Cortex-M знает только Thumb-2. Thumb-2 в основном обратно совместим с Thumb, по крайней мере на уровне сборки.


  • В некоторых архитектурах добавляются дополнительные инструкции.

Таким образом, если какой-то код скомпилирован с помощью компилятора, говорящего, что это для ARMv6, тогда компилятор может использовать одну из немногих инструкций с ARMv6, но не ARMv5. Это обычная ситуация, встречающаяся практически на всех платформах: например, если вы скомпилируете код C на ПК с GCC, используя флаг -march=core2, тогда результирующий двоичный файл может не работать на более старом процессоре Pentium.


  • Существует несколько соглашений о вызовах.

Соглашение о вызовах - это набор правил, которые определяют, как функции обмениваются параметрами и возвращаемыми значениями. Процессор знает только свои регистры и не имеет понятия о стеке. В соглашении вызовов указано, в каких параметрах регистров идет и как они закодированы (например, если есть параметр char, он идет в младших 8 бит регистра, но вызывающий должен очистить/подписать расширение 24 бит или нет?). Он описывает структуру стека и выравнивание. Он нормализует условия выравнивания и заполнение полей структуры.

Существует два основных соглашения для ARM: ATPCS (старый) и AAPCS (новый). Они отличаются друг от друга по значению значений с плавающей запятой. Для целочисленных параметров они в основном идентичны (но AAPCS требует более строгого выравнивания стека). Конечно, соглашения варьируются в зависимости от набора команд и наличия взаимодействия Thumb.

В некоторых случаях возможно иметь некоторый двоичный код, который соответствует как ATPCS, так и AAPCS, но это не является надежным и нет предупреждения о несоответствии. Итак, нижняя строка: вы не можете иметь истинную двоичную совместимость между системами, использующими различные соглашения о вызовах.


  • Существуют дополнительные сопроцессоры.

Архитектура ARM может быть дополнена дополнительными элементами, которые добавляют свои собственные инструкции в набор основных команд. FPU является таким дополнительным сопроцессором (и его очень редко встречают на практике). Другим сопроцессором является NEON, набор команд SIMD, обнаруженный на некоторых новых процессорах ARM.

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


Подводя итог, если у вас есть код C, перекомпилируйте его. Не пытайтесь повторно использовать код, скомпилированный для другой архитектуры или системы.

Ответ 2

Подумайте об этом ARM против ARM, как о wintel-компьютере против Intel Mac. Предположим, что на обоих компьютерах есть один и тот же чип Intel (семейство), поэтому части вашего C-кода можно было компилировать один раз и работать на обоих процессорах просто отлично. Где и почему ваши программы различаются, не имеет ничего общего с процессором Intel, но все, что связано с чипами и материнской платой вокруг него, а также операционной системой в этом случае.

С ARM против ARM большинство различий - это не ядро, а конкретная логика поставщика, которая окружает ядро. так что это вопрос с загрузкой, если ваш C-код - это приложение, вызывающее стандартные вызовы api, тогда он должен скомпилироваться на руке или Intel или PowerPC или что-то еще. Если ваше приложение разговаривает с микросхемой или периферийными устройствами, то независимо от типа процессора, одна плата, один чип будет меняться, и в результате ваш код C должен быть написан для этого чипа или материнской платы. Если вы компилируете двоичный файл для ARMv6, он может и будет иметь инструкции, считающиеся undefined, на ARMv4 и вызовет уловку. Если вы компилируете ARMv4, ARMv6 должен работать с ним просто отлично.

В лучшем случае, если вы находитесь в этом прикладном пространстве, то то, что вы, скорее всего, увидите, это просто различия в производительности. Некоторые из них связаны с вашим выбором в настройках компилятора. И иногда вы можете помочь с вашим кодом. Я рекомендую избегать делений и с плавающей запятой, где это возможно. Мне не нравятся множители, но если они будут нажаты, они будут умножаться вместо деления. x86 избавил нас от бесполезных доступов, если вы начнете сейчас с выровненного ввода-вывода, он сэкономит вам дорогу, когда вы попадете в другие чипы, которые также предпочитают выравнивать доступ, а также вы получаете бит от различных операционных систем и загрузчики настраивают ARM, чтобы реагировать, ни одна из которых не используется для x86. Аналогичным образом сохраните эту привычку, и ваш x86-код будет работать намного быстрее.

Получите копию ARM ARM (google: ARM Architectural Reference Manual, вы можете скачать ее бесплатно во многих местах, я не знаю, что такое текущий оборот, rev я или что-то может быть). Просмотрите набор инструкций ARM и убедитесь, что большинство инструкций поддерживаются на всех ядрах, а некоторые были добавлены с течением времени, например, divide и byteswap и т.д. Вы увидите, что между ядрами нечего бояться.

Подумайте с точки зрения системы, wintel против intel mac. ARM не делает чипы, они делают и лицензионные ядра. Большинство продавцов, которые используют ARM в своем чипе, имеют свой собственный специальный соус вокруг него. Таким образом, это похоже на wintel против mac с тем же процессором в середине, но совершенно другим, когда дело доходит до всего, что касается процессора и его использования. Он не останавливается с ядром ARM, ARM продает периферийные устройства, единицы с плавающей запятой, кеши и т.д. Так мало, если любые ARMv4 являются такими же, например. Если ваш код касается различий, у вас будут проблемы, если вы этого не сделаете.

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

Ответ 3

ARM его сам довольно совместим, если вы придерживаетесь кода пользователя (код ядра, разумеется, отличается). В среде с размещенной ОС вы, вероятно, будете придерживаться ARMv5 (процессоры ARM926).

Большая разница исходит от:

  • Поведение кэша сильно отличается. Кэш на некоторых ARM даже практически устранен, что может привести к перегрузкам процесса.
  • FPU поставляется в нескольких вариантах (VFP, NEON и более!). Многие меньшие процессоры даже не имеют FPU.
  • Режим громкоговорителя сильно изменился. Режим громкой связи между ARMv5 не переносится в Thumb2 (ARMv6 +), а также обратно совместим.

Ответ 4

Если разница действительно важна для вас, вы должны понять это из публичной документации ARM.

Но весь смысл написания на языке более высокого уровня (даже если он только как "высокий", как C), должен не беспокоиться об этом. Все, что вы делаете, это перекомпилировать. Даже внутри ядра не так уж и нужно писать в сборке; и когда вам нужно что-то писать в сборке (т.е. не только для получения максимальной производительности), это обычно из-за не только выбора процессора (например, того, что было непосредственно отображено в памяти где?).

Ответ 5

Очень быстрый и грязный список областей для проверки при портировании между архитектурами в целом:

  • Endianness: использование профсоюза, литье типов данных, битподы, обмен данными
  • Выравнивание: требования к выравниванию, а также характеристики производительности возможного неравномерного доступа
  • Модель памяти: слабая и сильная?
  • Многоядерный: как работает когерентность?
  • Разное: подписанные типы с неподписанными типами данных, упаковка структуры данных, использование стека, тип данных enum...