Система команд Thumb-2 для Cortex-M
Справочник по системе команд Thumb-2 ядер Cortex-M: все команды Cortex-M3 по группам с флагами, правила второго операнда и констант, условное выполнение и блок IT, дальность переходов, выравнивание, деление и насыщение, что добавляет Cortex-M4 и чего нет у Cortex-M0. По программным руководствам ST PM0056, PM0214 и PM0215, с примерами на ассемблере.
Обновлено Редакция GAW
Thumb-2 — система команд ядер ARM Cortex-M. В ней 16-битные команды классического набора Thumb смешаны с 32-битными: короткие дают плотный код, длинные — доступ ко всем регистрам, большие константы, деление, битовые поля и условное выполнение. Ядра Cortex-M выполняют только Thumb-2, состояния ARM с 32-битными командами, как у ARM7TDMI, у них нет. Ассемблер сам выбирает длину каждой команды, а программист пишет на едином языке ассемблера UAL.
Полный набор — у ядер ARMv7-M: Cortex-M3, M4 и M7. Cortex-M4 добавляет к нему команды цифровой обработки сигналов и, если есть FPU, команды плавающей точки (PM0214). Cortex-M0 и M0+ (ARMv6-M) выполняют подмножество — около полусотни команд, почти все 16-битные (PM0215). Здесь — команды Cortex-M3 по программному руководству ST PM0056 с пометками, что есть у других ядер. Устройство ядра, регистры и такты команд разобраны в статье об архитектуре Cortex-M3, сравнение ядер — на странице ARM Cortex-M.
Коротко
- Регистры: R0–R12, SP (R13), LR (R14), PC (R15); большинство 16-битных команд работает только с R0–R7.
- Флаги N, Z, C, V и Q в APSR; команды обработки данных меняют их только с суффиксом S.
- Условное выполнение: условные переходы
B<cond>или блок IT до четырёх команд. - Константы: 8-битное значение со сдвигом или шаблоны 0x00XY00XY, 0xXY00XY00, 0xXYXYXYXY; любое 32-битное число — MOVW и MOVT.
- Переходы: B и BL — ±16 МБ, условный B вне блока IT — ±1 МБ, CBZ и CBNZ — вперёд на 4–130 байт.
- Деление SDIV и UDIV — с округлением к нулю, деление на ноль даёт 0, если не включена ловушка DIV_0_TRP.
Регистры и операнды
Команды обработки данных записываются в виде op{S}{cond} Rd, Rn, Operand2: Rd — приёмник, Rn — первый операнд, Operand2 — гибкий второй операнд (PM0056, раздел 3.3.3). Если Rd не указан, приёмником служит Rn. Второй операнд может быть:
- константой
#constant: 8-битное значение, сдвинутое влево на любое число бит в пределах 32-битного слова, или число вида 0x00XY00XY, 0xXY00XY00, 0xXYXYXYXY, где X и Y — шестнадцатеричные цифры. Например, 0x3FC (0xFF, сдвинутое на 2 бита) и 0x00AB00AB допустимы, а 0x101 — нет. Если константа недопустима, ассемблер может подставить равносильную команду:CMP Rd, #0xFFFFFFFEпревращается вCMN Rd, #0x2; - регистром со сдвигом
Rm {, shift}: ASR #n и LSR #n — сдвиг вправо на 1–32 бита, LSL #n — влево на 1–31, ROR #n — вращение на 1–31, RRX — вращение на один бит через флаг C. Сдвиг применяется к значению, а сам Rm не меняется.
Шире константы у немногих команд: ADDW и SUBW принимают 12-битное число 0–4095, MOVW — 16-битное, а MOVT записывает 16 бит в старшую половину регистра, не трогая младшую. Пара MOVW и MOVT загружает любое 32-битное значение двумя командами (PM0056, раздел 3.5.7).
Логические команды с суффиксом S (ANDS, ORRS, EORS, BICS, ORNS, MOVS, MVNS, TST, TEQ) с константой больше 255, полученной сдвигом 8-битного значения, меняют и флаг C; с другими константами C не меняется.
Флаги и условное выполнение
| Флаг | Когда устанавливается |
|---|---|
| N | результат отрицательный (бит 31 = 1) |
| Z | результат равен нулю |
| C | перенос при сложении (результат ≥ 2³²), отсутствие заёма при вычитании, выдвинутый бит при сдвиге |
| V | переполнение со знаком: результат сложения, вычитания или сравнения ≥ 2³¹ или меньше −2³¹ |
| Q | было насыщение в SSAT и USAT (у Cortex-M4 — и в командах DSP); «липкий» флаг, сбрасывается только программой |
Большинство команд обновляют флаги только с суффиксом S; CMP, CMN, TST и TEQ меняют их всегда и никуда не пишут результат (PM0056, раздел 3.3.7). Условие проверяется суффиксом:
| Суффикс | Флаги | Значение |
|---|---|---|
| EQ / NE | Z = 1 / Z = 0 | равно / не равно |
| CS (HS) / CC (LO) | C = 1 / C = 0 | без знака ≥ / без знака < |
| MI / PL | N = 1 / N = 0 | отрицательное / неотрицательное |
| VS / VC | V = 1 / V = 0 | переполнение / нет переполнения |
| HI / LS | C = 1 и Z = 0 / C = 0 или Z = 1 | без знака > / без знака ≤ |
| GE / LT | N = V / N ≠ V | со знаком ≥ / со знаком < |
| GT / LE | Z = 0 и N = V / Z = 1 или N ≠ V | со знаком > / со знаком ≤ |
| AL | любые | всегда, по умолчанию |
Таблица — по PM0056 (таблица 23). Условными без дополнительных команд бывают только переходы B<cond>. Остальные команды становятся условными внутри блока IT (If-Then): команда IT{x{y{z}}} cond делает условными до четырёх следующих команд, где x, y и z — T (условие cond) или E (обратное условие) для второй, третьей и четвёртой команды (PM0056, раздел 3.8.7). Внутри блока нельзя использовать IT, CBZ, CBNZ, CPSID и CPSIE, а команда, меняющая PC, может стоять только последней. Многие ассемблеры вставляют IT сами, если встречают условную команду вне блока.
Модуль числа и условное присваивание — примеры из PM0056:
MOVS R0, R1 @ R0 = R1, set N and Z
IT MI @ the next instruction runs if negative
RSBMI R0, R1, #0 @ R0 = -R1
CMP R0, R1 @ signed compare
ITT GT @ two instructions on "greater than"
CMPGT R2, R3 @ compare the second pair only if R0 > R1
MOVGT R4, R5 @ R4 = R5 if both pairs are "greater than"
Ширина команды: .N и .W
Многие команды имеют и 16-битную, и 32-битную кодировку, и ассемблер выбирает короткую, если позволяют регистры и константа. Суффикс .W заставляет выбрать 32-битную форму, .N — 16-битную; если её нет, ассемблер выдаёт ошибку (PM0056, раздел 3.3.8). .W бывает нужен для переходов на далёкие метки, где ассемблер не может сам выбрать длину: BCS.W label, ADDS.W R0, R0, R1.
Команды Cortex-M3 по группам
| Группа | Команды | Флаги | Cortex-M0 |
|---|---|---|---|
| Сложение и вычитание | ADD, ADDW, ADC, SUB, SUBW, SBC, RSB | N, Z, C, V | ADD, ADCS, SUB, SBCS; RSBS только с #0 |
| Логика | AND, ORR, EOR, BIC, ORN | N, Z, C | ANDS, ORRS, EORS, BICS; ORN нет |
| Сдвиги | ASR, LSL, LSR, ROR, RRX | N, Z, C | ASRS, LSLS, LSRS, RORS; RRX нет |
| Сравнение и проверка | CMP, CMN, TST, TEQ | N, Z, C, V | CMP, CMN, TST; TEQ нет |
| Пересылка | MOV, MVN, MOVW, MOVT | N, Z, C у MOV и MVN | MOV, MOVS, MVNS; MOVW и MOVT нет |
| Перестановка битов и байтов | REV, REV16, REVSH, RBIT, CLZ | — | REV, REV16, REVSH; RBIT и CLZ нет |
| Битовые поля и расширение | BFC, BFI, SBFX, UBFX, SXTB, SXTH, UXTB, UXTH | — | только SXTB, SXTH, UXTB, UXTH |
| Умножение | MUL, MLA, MLS, UMULL, UMLAL, SMULL, SMLAL | N, Z у MULS | только MULS |
| Деление | SDIV, UDIV | — | нет |
| Насыщение | SSAT, USAT | Q | нет |
| Загрузка и сохранение | LDR, LDRB, LDRH, LDRSB, LDRSH, LDRD, STR, STRB, STRH, STRD, формы с T для непривилегированного доступа, ADR | — | LDR, LDRB, LDRH, LDRSB, LDRSH, STR, STRB, STRH, ADR; LDRD, STRD нет |
| Множественная загрузка и стек | LDM, LDMDB, STM, STMDB, PUSH, POP | — | LDM, STM, PUSH, POP |
| Монопольный доступ | LDREX, LDREXB, LDREXH, STREX, STREXB, STREXH, CLREX | — | нет |
| Переходы | B, BL, BX, BLX, CBZ, CBNZ, TBB, TBH, IT | — | B, BL, BX, BLX; CBZ, CBNZ, TBB, TBH и IT нет |
| Управление | CPSID, CPSIE, MRS, MSR, DMB, DSB, ISB, SVC, BKPT, NOP, SEV, WFE, WFI | N, Z, C, V у MSR | все |
Таблица собрана по PM0056 (таблица 20) и PM0215 (таблица 14). У Cortex-M0 почти все команды обработки данных работают только с регистрами R0–R7 и всегда меняют флаги (отсюда суффикс S в мнемониках), а условными бывают только переходы.
Некоторые команды недоступны из C напрямую, и CMSIS даёт для них встроенные функции: __enable_irq() и __disable_irq() (CPSIE I и CPSID I), __DSB(), __DMB(), __ISB(), __REV(), __REV16(), __REVSH(), __RBIT(), __SEV(), __WFE(), __WFI() (PM0056, таблица 21).
Переходы
| Команда | Дальность |
|---|---|
| B label | ±16 МБ |
B<cond> label вне блока IT | ±1 МБ |
B<cond> label внутри блока IT | ±16 МБ |
| BL label | ±16 МБ |
| BX Rm, BLX Rm | любой адрес из регистра |
| CBZ, CBNZ Rn, label | вперёд на 4–130 байт, только R0–R7 |
Таблица — по PM0056 (разделы 3.8.5 и 3.8.6); для наибольшей дальности может понадобиться суффикс .W. У адреса перехода в BX и BLX бит 0 должен быть равен 1 — это признак состояния Thumb; с нулевым битом ядро вызывает отказ при выполнении следующей команды. Компоновщик ставит этот бит в адресах функций сам.
CBZ и CBNZ сравнивают регистр с нулём и переходят, не меняя флагов, — удобно для циклов и проверок указателей. TBB и TBH — переход по таблице смещений: TBB берёт байт, TBH — полуслово из таблицы по адресу Rn с индексом Rm, удваивает его и прибавляет к адресу за командой (PM0056, раздел 3.8.8). Так компилятор реализует плотные switch.
Загрузка, сохранение и выравнивание
Обращения к памяти идут только через LDR и STR в разных вариантах: со смещением-константой, с регистровым смещением со сдвигом, с пред- и постинкрементом базового регистра, относительно PC для констант в памяти. LDRD и STRD пересылают два слова, LDM и STM — список регистров, PUSH и POP — синонимы STMDB и LDMIA с адресом в SP и обновлением SP (PM0056, раздел 3.4.7).
Невыровненный доступ Cortex-M3 разрешает только командам LDR, LDRT, LDRH, LDRHT, LDRSH, LDRSHT, STR, STRT, STRH и STRHT. Остальные — LDRD, STRD, LDM, STM, PUSH, POP, LDREX и STREX — при невыровненном адресе вызывают отказ UsageFault. ARM рекомендует выравнивать данные: невыровненный доступ медленнее, а некоторые области памяти его не поддерживают; бит UNALIGN_TRP регистра SCB_CCR включает ловушку и на разрешённые невыровненные обращения (PM0056, раздел 3.3.5).
LDREX и STREX — монопольный доступ для семафоров и атомарных счётчиков: STREX записывает значение, только если с момента LDREX никто не обращался к этому адресу, и кладёт в регистр результата 0 при успехе и 1 при неудаче (PM0056, раздел 3.4.8). Адрес и размер данных у пары команд должны совпадать.
Умножение, деление и насыщение
MUL, MLA и MLS дают 32-битный результат, UMULL, SMULL, UMLAL и SMLAL — 64-битный в паре регистров. SDIV и UDIV делят 32-битные числа со знаком и без знака, результат округляется к нулю (PM0056, раздел 3.6.3). Деление на ноль по умолчанию возвращает 0, а если установить бит DIV_0_TRP регистра SCB_CCR, вызывает отказ UsageFault (PM0056, раздел 4.4.7). У Cortex-M0 аппаратного деления нет, и компилятор вызывает библиотечную функцию.
SSAT и USAT ограничивают значение n-битным диапазоном: SSAT — от −2ⁿ⁻¹ до 2ⁿ⁻¹ − 1 (n = 1…32), USAT — от 0 до 2ⁿ − 1 (n = 0…31), при необходимости после сдвига. Если значение пришлось ограничить, ставится флаг Q (PM0056, раздел 3.7.1). Так одним тактом решается задача «обрезать результат фильтра до 12 бит ЦАП»: USAT R0, #12, R0.
Битовые поля и перестановки
Битовые команды заменяют связки сдвигов и масок, которые на 8-битных микроконтроллерах занимают несколько команд:
| Команда | Действие | На C |
|---|---|---|
UBFX Rd, Rn, #lsb, #width | извлечь поле без знака | (x >> lsb) & ((1u << width) - 1) |
SBFX Rd, Rn, #lsb, #width | извлечь поле со знаковым расширением | поле как знаковое число |
BFI Rd, Rn, #lsb, #width | вставить младшие биты Rn в поле Rd | d = (d & ~mask) | ((n << lsb) & mask) |
BFC Rd, #lsb, #width | обнулить поле | d &= ~mask |
CLZ Rd, Rm | число ведущих нулей | 31 − ⌊log₂ x⌋ для x > 0 |
RBIT Rd, Rn | обратный порядок битов | __RBIT(x) |
REV Rd, Rn | обратный порядок байтов слова | __REV(x), смена порядка байтов |
Компиляторы сами используют UBFX, BFI и CLZ для подходящих выражений и для __builtin_clz. Именно через RBIT и CLZ драйверы LL STM32 вычисляют номер вывода из маски — макрос POSITION_VAL, разобранный в статье о HAL, LL и регистрах.
Пример: записать 4-битное значение из R1 в биты 8–11 регистра R0 и прочитать их обратно.
BFI R0, R1, #8, #4 @ R0[11:8] = R1[3:0], other bits unchanged
UBFX R2, R0, #8, #4 @ R2 = R0[11:8]
Что добавляет Cortex-M4
Cortex-M4 выполняет все команды Cortex-M3 и добавляет две группы (PM0214, глава 3):
- DSP и SIMD — операции над парами полуслов или четвёрками байтов в одном регистре: SADD16, SADD8, UADD16, UADD8, SSUB16, USUB8, их варианты с делением пополам (SHADD16, UHADD8) и с насыщением (QADD16, UQADD8), обмен половин SASX и SSAX; умножение полуслов с накоплением SMLA, SMLAW, SMLAD, SMLSD, SMLALD; старшая половина произведения SMMUL, SMMLA; насыщающее сложение QADD, QSUB, QDADD; упаковка PKHBT и PKHTB; расширение с прибавлением SXTAB, UXTAH; SSAT16 и USAT16. На них построены функции библиотеки CMSIS-DSP для фильтров и БПФ.
- FPU одинарной точности (Cortex-M4F) — команды с префиксом V: VADD.F32, VSUB, VMUL, VDIV, VSQRT, VABS, VNEG, умножение с накоплением VMLA и VMLS и слитное VFMA и VFMS, сравнение VCMP, преобразования VCVT между целыми, фиксированной точкой и плавающей, пересылки VMOV, загрузка и сохранение VLDR, VSTR, VLDM, VSTM, VPUSH, VPOP, доступ к регистру состояния FPSCR через VMRS и VMSR.
Чего нет у Cortex-M0 и M0+
Cortex-M0 реализует архитектуру ARMv6-M — 16-битный Thumb с элементами Thumb-2 (PM0215). По сравнению с Cortex-M3 у него нет: блока IT и условного выполнения команд, кроме переходов; CBZ, CBNZ, TBB и TBH; аппаратного деления; MOVW и MOVT; битовых полей BFI, BFC, UBFX, SBFX; RBIT и CLZ; длинного умножения UMULL и SMULL; насыщения SSAT и USAT; монопольного доступа LDREX и STREX; LDRD и STRD; гибкого второго операнда со сдвигом и большинства форм с константами. Поэтому один и тот же код на C для Cortex-M0 получается длиннее и медленнее, а атомарные операции делают запретом прерываний.
Как читать ассемблер компилятора
Даже без программирования на ассемблере полезно уметь прочитать листинг: так видно, во что превратился код на C, где компилятор вызвал библиотечное деление, где появился лишний доступ к volatile. Листинг даёт arm-none-eabi-objdump -d или окно дизассемблера отладчика. Типичные признаки:
PUSH {R4-R7, LR}иPOP {R4-R7, PC}— пролог и эпилог функции: сохранены регистры, которые функция обязана сохранить, и адрес возврата;LDR R0, [PC, #offset]илиLDR R0, =value— константа из пула литералов после функции;BL __aeabi_uidiv— деление в библиотеке: на Cortex-M0 это норма, на Cortex-M3 — признак того, что код собран не для того ядра;DSBиISBпосле записи в регистры ядра — барьеры, которые вставили функции CMSIS.
Для перевода констант между системами счисления пригодится конвертер чисел.
Частые вопросы
Чем Thumb-2 отличается от Thumb?
Классический Thumb — набор 16-битных команд, который у ARM7TDMI работал рядом с 32-битным набором ARM и не давал доступа ко всем возможностям процессора. Thumb-2 дополняет 16-битные команды 32-битными: в одном потоке смешаны команды обеих длин, и 32-битные дают доступ ко всем регистрам, большие константы, деление, битовые поля и условное выполнение блоком IT. Ядра Cortex-M выполняют только Thumb-2 — переключаться в состояние ARM им не нужно.
Какие константы можно использовать в командах Cortex-M3?
Во втором операнде большинства команд обработки данных — 8-битное значение, сдвинутое влево на любое число бит в пределах 32-битного слова, а также числа вида 0x00XY00XY, 0xXY00XY00 и 0xXYXYXYXY (PM0056, раздел 3.3.3). ADDW и SUBW принимают 12-битную константу, MOVW — 16-битную. Любое 32-битное число загружается парой MOVW и MOVT или из памяти командой LDR.
Сколько команд можно сделать условными командой IT?
До четырёх: IT, ITT, ITE, ITTT, ITTE и так далее, где каждая буква после первой T — Then или Else для следующей команды. Внутри блока нельзя использовать IT, CBZ, CBNZ, CPSID и CPSIE, а переход или запись в PC может быть только последней командой блока (PM0056, раздел 3.8.7).
Что вернёт деление на ноль на Cortex-M3?
Ноль, если бит DIV_0_TRP регистра SCB_CCR сброшен — так по умолчанию. Если его установить, SDIV или UDIV с нулевым делителем вызывают отказ UsageFault (PM0056, раздел 4.4.7). Результат деления всегда округляется к нулю.
Источники
- STMicroelectronics. PM0056 Rev 7. STM32F10xxx/20xxx/21xxx/L1xxxx Cortex-M3 programming manual — 3.1 — таблица 20, все команды Cortex-M3 с флагами; 3.2 — встроенные функции CMSIS (таблица 21); 3.3.3 — второй операнд и константы; 3.3.4 — сдвиги; 3.3.5 — выравнивание; 3.3.7 — флаги и условия (таблица 23); 3.3.8 — суффиксы .N и .W; 3.5.7 — MOVT; 3.6.3 — SDIV и UDIV; 3.7.1 — SSAT и USAT; 3.8.5 — дальность переходов; 3.8.6 — CBZ и CBNZ; 3.8.7 — IT; 3.8.8 — TBB и TBH; 4.4.7 — DIV_0_TRP и UNALIGN_TRP
- STMicroelectronics. PM0214 Rev 10. STM32 Cortex-M4 MCUs and MPUs programming manual — 3.5–3.8 — команды DSP: SIMD-сложение и вычитание байтов и полуслов, умножение полуслов SMLA, SMLAD, SMMUL, насыщающая арифметика QADD, QSUB, упаковка PKHBT; 3.10 — команды FPU одинарной точности от VABS до VSQRT
- Arm. Run-time ABI for the Arm Architecture (RTABI32) и Procedure Call Standard (AAPCS32) — функции деления __aeabi_uidiv и __aeabi_idiv; регистры R4–R11, которые сохраняет вызываемая функция
- STMicroelectronics. PM0215 Rev 2. STM32F0 series Cortex-M0 programming manual — ARMv6-M на основе 16-битного Thumb с технологией Thumb-2; таблица 14 — все команды Cortex-M0
Нашли ошибку?
Смотрите также
- ARM Cortex-MЯдра M0–M85: архитектуры, FPU, DSP, MPU, TrustZone, выбор
- Cortex-M3Регистры, карта памяти, bit-banding, NVIC, SysTick, MPU, старт, пример
- HAL, LL или регистрыУровни STM32Cube, что делает HAL, правила совмещения, один код тремя способами
- STM32Распиновки по корпусам, альтернативные функции, похожие по выводам МК
- Система команд AVRВсе инструкции с флагами и тактами, адресация, примеры кода
- Конвертер HEX, BIN, DECДополнительный код, float, байты и биты