КулЛиб - Классная библиотека! Скачать книги бесплатно 

Практический курс микропроцессорной техники на базе процессорных ядер ARM-Cortex-M3/M4/M4F [Владимир Филиппович Козаченко] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]
Козаченко В.Ф., Алямкин Д.И., Анучин А.С.,
Жарков А.А., Лашкевич М.М.,
Савкин Д.И., Шпак Д.М.

Практический курс
микропроцессорной техники на базе
процессорных ядер ARM-CortexM3/M4/M4F
Архитектура, система команд, разработка и отладка
программного обеспечения на Ассемблере в интегрированной
среде Keil μVision

МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ
РОССИЙСКОЙ ФЕДЕРАЦИИ
__________________________________________________________________
НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ УНИВЕРСИТЕТ «МЭИ»
__________________________________________________________________

Практический курс микропроцессорной техники
на базе процессорных ядер
ARM-Cortex-M3/M4/M4F
по курсам «Микропроцессорные средства в электротехнике»,
«Компьютерная и микропроцессорная техника», «Микропроцессорная
техника в электроприводе» и др. для студентов, обучающихся по
направлениям «Электроэнергетика и электротехника» (13.03.02 –
бакалавриат, 13.04.02 – магистратура), «Электроника и наноэлектроника»
(11.03.04 – бакалавриат, 11.04.04 – магистратура).
Под общ. редакцией д.т.н., проф. В.Ф. Козаченко
Учебное пособие
Текстовое электронное издание

Москва
Издательство МЭИ
2019

УДК 621.398
ББК 32.973
П 692
Утверждено учебным управлением НИУ «МЭИ» в качестве учебного
издания
Подготовлено на кафедре автоматизированного электропривода НИУ
«МЭИ»
Рецензенты: докт. техн. наук, проф. каф. электротехники и промышленной
электроники МГТУ им. Баумана А.Б. Красовский,
докт. техн. наук, проф. каф. АЭП НИУ «МЭИ» М.Г. Бычков
Авторы: В.Ф. Козаченко, А.С. Анучин, Д.И. Алямкин, А.А. Жарков, М.М.
Лашкевич, Д.И. Савкин, Д.М. Шпак
П 692

Практический курс микропроцессорной техники на базе процессорных ядер
ARM-Cortex-M3/M4/M4F [электронный ресурс]: учебное пособие – электрон.
текстовые дан. (12 Мб) / В.Ф. Козаченко, А.С. Анучин, Д. И. Алямкин и др.;
под общ. ред. В.Ф. Козаченко. – М.: Издательство МЭИ, 2019. – 1 электрон.
опт. диск (CD-ROM).
ISBN 978-5-7046-2165-2

Практический интерактивный курс микропроцессорной техники для встраиваемых
применений на базе микроконтроллеров с процессорными ядрами ARM-CortexM3/M4/M4F, выпускаемых в том числе отечественными предприятиями. Является
одновременно учебником, сборником лабораторно-практических работ, самоучителем и
справочником по архитектуре, системе команд и технологии разработки программного
обеспечения на Ассемблере с использованием интегрированной среды разработки и
отладки Keil μVision.
Ориентирован на разработчиков цифровых систем управления в энергетике, в
транспорте, в станкостроении и робототехнике. Предназначен для студентов большинства
электротехнических специальностей, в том числе: «Электропривод и автоматика»,
«Электрический
транспорт»,
«Электрооборудование
автономных
объектов»,
«Промышленная электроника» и др.
Издано и опубликовано в авторской редакции.
Минимальные системные требования:
Компьютер: процессор х86 с тактовой частотой 500 МГц и выше; ОЗУ 512 Мб; 20 Мб на
жестком диске; видеокарта SVGA 1280x1024 High Color (32 bit);
Операционная система: Windows ХР/7/8 и старше
Программное обеспечение: Adobe Acrobat Reader версии 6 и старше.
Доступ к сети интернет.
Номер свидетельства о государственной регистрации: № 0321901060 от 12 апреля 2019 г.
ISBN 978-5-7046-2165-2
©
Авторы, 2019
©
Национальный
исследовательский
университет «МЭИ», 2019

СОДЕРЖАНИЕ

СОДЕРЖАНИЕ
СОДЕРЖАНИЕ .......................................................................................................................................... 3
ПРЕДИСЛОВИЕ ........................................................................................................................................ 4
ГЛАВА 1. ПОЧЕМУ ARM? .................................................................................................................... 10
ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ ................................................... 23
ГЛАВА 3. ОСНОВЫ ДВОИЧНОЙ АРИФМЕТИКИ ........................................................................... 38
ГЛАВА 4. ОСНОВНЫЕ ТИПЫ ПРОЦЕССОРНЫХ АРХИТЕКТУР ................................................. 50
ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/M4/M4F ...................... 60
ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM ............... 76
ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX M ............................................. 91
ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ КРОСССРЕДСТВ ................................................................................................................................................ 128
ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ .............................. 155
ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТРАХ ЦПУ И ПАМЯТИ........................................... 176
ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ ................................................................................ 210
ГЛАВА 12. РАБОТА С БИТОВЫМИ ПЕРЕМЕННЫМИ. РЕАЛИЗАЦИЯ ЛОГИЧЕСКИХ
КОНТРОЛЛЕРОВ И ДИСКРЕТНЫХ АВТОМАТОВ ....................................................................... 239
ГЛАВА 13. ИСПОЛЬЗОВАНИЕ БИТОВЫХ ОБЛАСТЕЙ ПАМЯТИ ДАННЫХ И РЕГИСТРОВ
ПЕРИФЕРИЙНЫХ УСТРОЙСТВ ....................................................................................................... 274
ГЛАВА 14. РАБОТА СО СТЕКОМ. ВЛОЖЕННЫЕ ПОДПРОГРАММЫ ..................................... 286
ГЛАВА 15. ПРОГРАММНАЯ РЕАЛИЗАЦИЯ НА ЯЗЫКЕ АССЕМБЛЕРА ТИПОВЫХ
АЛГОРИТМИЧЕСКИХ СТРУКТУР .................................................................................................. 308
ГЛАВА 16. ВВЕДЕНИЕ В ЦИФРОВУЮ ОБРАБОТКУ СИГНАЛОВ ........................................... 340
ГЛАВА 17. КОМАНДЫ ЦИФРОВОЙ ОБРАБОТКИ СИГНАЛОВ ................................................ 374
ГЛАВА 18. КОМАНДЫ ПРЕДВАРИТЕЛЬНОЙ ОБРАБОТКИ ДАННЫХ ................................... 405
ГЛАВА 19. ВВЕДЕНИЕ В АРИФМЕТИКУ ЧИСЕЛ С ПЛАВАЮЩЕЙ ТОЧКОЙ ...................... 423
ГЛАВА 20. МОДУЛЬ ПОДДЕРЖКИ ВЫЧИСЛЕНИЙ С ПЛАВАЮЩЕЙ ТОЧКОЙ FPU .......... 444
ГЛАВА 21. КОМАНДЫ РАБОТЫ С ЧИСЛАМИ В ФОРМАТЕ С ПЛАВАЮЩЕЙ ТОЧКОЙ ... 462
ГЛАВА 22. ПОДДЕРЖКА ЦИФРОВОГО РЕГУЛИРОВАНИЯ И ФИЛЬТРАЦИИ С
ПЛАВАЮЩЕЙ ТОЧКОЙ .................................................................................................................... 501
ПРИЛОЖЕНИЕ 1. ИЛЛЮСТРИРОВАННЫЙ СПРАВОЧНИК ПО СИСТЕМЕ КОМАНД
ПРОЦЕССОРНЫХ ЯДЕР CORTEX-M3/M4/M4F ............................................................................. 538
ПРИЛОЖЕНИЕ 2. ПРОЕКТЫ ............................................................................................................. 539
ПРИЛОЖЕНИЕ 3. ОТЛАДОЧНЫЙ КОМПЛЕКС VECTORCARD ................................................ 540

3

ПРЕДИСЛОВИЕ

ПРЕДИСЛОВИЕ
ARM-процессоры для встроенного управления
Фирма ARM начала активное проникновение на рынок встраиваемых приложений
и систем управления оборудованием с создания серии специализированных процессоров
на базе ядра Cortex-M. Если процессоры ARM11, Cortex-A9 (семейство Cortex-A) больше
ориентированы на рынок портативных цифровых устройств, таких как мобильные
телефоны, планшеты, игровые приставки, где требуется большая вычислительная
мощность, сравнимая с мощностью процессоров для современных компьютеров, при
малом потреблении, то ядро Cortex-M предназначено для создания микроконтроллеров,
встраиваемых в промышленное оборудование — от преобразователей частоты до роботов
и автоматизированных линий. Третье направление развития процессоров ARM (семейство
Cortex-R) ориентировано на создание высокопроизводительных систем реального
времени для автомобильной промышленности, авиации и других, ответственных,
применений, таких как оборудование энергетики и атомной промышленности.
Семейство Cortex-M появилось как альтернатива большому количеству 8- и 16разрядных микроконтроллеров, присутствующих на рынке, преимуществом которого
является существенный рост производительности вычислительных операций при
сохранении низкого потребления электроэнергии. Типовые применения: управление
силовыми преобразователями и источниками питания, электрическими двигателями,
автомобильной электроникой, бытовыми устройствами, распределенное управление
оборудованием «умных» домов, комплексная автоматизация производственных процессов
и т.д.
На протяжении более чем двух десятилетий на рынке микроконтроллеров
законодателями мод были хорошо известные изделия Intel 8051 и Motorola 68HC11/12/16.
Начиная с 90-х годов значительное развитие получили 16-разрядные микроконтроллеры, в
том числе сигнальные, которые имеют не только высокопроизводительное
вычислительное ядро, но и широкий спектр встроенных на кристалл периферийных
устройств. И вот, наконец-то, пришла очередь 32-разрядных микроконтроллеров
завоевывать этот рынок. Фирма ARM поставила перед собой задачу создать процессорное
ядро, которое одинаково хорошо будет работать и в системе управления холодильником
или компрессором, и в современном мобильном телефоне.
Если тактовые частоты первых процессоров ARM были на уровне нескольких МГц,
то сейчас они возросли на два и более порядка. Это позволило использовать архитектуру
ARM в мобильных телефонах и портативных компьютерах, создав существенную
конкуренцию традиционным производителям микропроцессоров, таким, как Intel.
Оказалось, что мощный 32-разрядный процессор способен эффективно решать
большинство задач, возникающих в сотовой связи — от интерфейса пользователя с
клавиатурой и экраном до мониторинга состояния аккумулятора, декодирования MP3музыки и поддержки компьютерных игр. И все это – при умеренном потреблении энергии.
По сути микропроцессоры ARM7 (встроенные впервые в планшет Apple iPod),
кардинально изменили ситуацию на рынке мобильных устройств, сделав их доступными
миллионам людей по всему миру.
На базе ARM7 и было разработано семейство процессорных ядер Cortex-M.
Начиная с 2010 г. поставки микроконтроллеров этого семейства резко возрастают.
Ожидается, что эта тенденция будет господствовать в текущем десятилетии, и к его концу
традиционные 8- и 16-разрядные микроконтроллеры практически не будут использоваться
в новых разработках. Не последняя роль при этим отводится языкам высокого уровня

4

ПРЕДИСЛОВИЕ
С/C++, трансляторы с которых стали совершенными и доступными большинству
разработчиков, что существенно ускорило процесс от начала разработки до выхода
конечных изделий на рынок.
Если раньше использовались в основном однопроцессорные решения, то сейчас
ситуация изменилась. Оказалось, что интеграция на кристалл сразу нескольких
процессоров (от 2 до 8), возможно с разной архитектурой и встроенной периферией,
позволяет предельно эффективно решать весь комплекс задач. Каждый процессор делает
то, для чего оптимизирована его архитектура и система команд. Это направление
особенно бурно развивается для использования в смартфонах, которые стали
многофункциональными портативными компьютерами, в том числе для решения задач
реального управления (например, оборудованием «умного» дома).
Мультипроцессорные системы получили название «Системы на кристалле» Systemon-Chip (SoC) и могут содержать не только центральный процессор и сопроцессор
поддержки вычислений с плавающей точкой, но и большое число системных
периферийных устройств. Так, процессор фирмы Texas Instruments OMAP5 имеет на
борту двухядерный процессор Cortex-A15 и два микроконтроллера Cortex-M4. При
разработке программного обеспечения для таких сложных систем большое значение
имеет унификация языков программирования отдельных процессоров, что может быть
реализовано только в изделиях одного производителя.

Нужно ли современному инженеру знание архитектуры
процессора, его системы команд, языка Ассемблер?
Резкое увеличение сложности процессорной техники ставит перед инженерами
вопрос: «А нужно ли вообще изучать архитектуру и систему команд конкретного
процессора, учиться программированию на Ассемблере? Может быть достаточно знать
только язык высокого уровня СИ, чтобы эффективно работать?». Мы считаем, что
актуальность знания основ процессорной техники только возрастает:
1. Не зная архитектуры и внутреннего устройства микроконтроллера невозможно создать
на его основе микропроцессорный контроллер, встраиваемый в конкретное изделие, в
котором будут эффективно использованы как возможности ядра процессора, так и
встроенной в него периферии.
2. Пользуясь языком высокого уровня, Вы работаете с процессором как с «черным
ящиком», который должен уметь делать все, что Вы для него запрограммировали.
Однако, далеко не факт, что используемый в конкретном трансляторе режим работы
процессора оптимален для Вашего приложения (например, используемый режим
округления при работе с числами с плавающей точкой). Вы можете получить
неожиданный результат и даже не понять в чем ошибка.
3. Система команд ARM-процессоров столь совершенна, что часто одной команды на
Ассемблере достаточно для решения задачи, для которой на языке высокого уровня
потребуется специализированная библиотека. Например, преобразование числа в
формате с фиксированной точкой в число с плавающей точкой или обратно.
4. Работа с портами ввода/вывода и периферийными устройствами, встроенными на
кристалл микроконтроллера, порой более эффективна на Ассемблере, чем на языке
высокого уровня, особенно если для этого используется по-битово адресуемая память.
Часто разработчики даже не подозревают, что используемый ими микроконтроллер
имеет на борту «битовый сопроцессор», позволяющий предельно эффективно
реализовывать логические контроллеры и дискретные управляющие автоматы.
5. Окончательная оптимизация микропроцессорной системы по быстродействию, объему
используемой памяти и энергопотреблению возможна только на нижнем уровне. В
5

ПРЕДИСЛОВИЕ
этом случае программа на языке С/С++ подвергается анализу с точки зрения наиболее
узких мест и эти места оптимизируются с использованием макросов или подпрограмм,
написанных на Ассемблере.
6. Не зная архитектуру процессора и его систему команд невозможно создать
специальное программное обеспечение: компиляторы; драйверы периферийных
устройств, специализированные библиотеки подпрограмм, например, для векторного
управления электрическими двигателями.
7. Возможности современных процессоров и микроконтроллеров в части специальных
команд цифровой обработки сигналов типа «умножение с накоплением» позволяют
сегодня на Ассемблере в реальном времени решать в несколько раз более сложные
системы дифференциальных уравнений, чем с использованием языка C/C++. Это
открывает широкие возможности для использования принципиально новых методов
цифрового управления оборудованием: с наблюдателями состояния, с прогнозом
траектории движения и т.д.
Таким образом, инженер-аппаратчик и инженер-конструктор без знания основ
процессорной техники не сможет оптимально проектировать прикладные устройства, а
инженер-программист, который знает не только язык высокого уровня, но и архитектуру и
систему команд процессора, будет тратить в разы меньше времени на оптимизацию кода и
отладку конкретного проекта.
Итак, семейство микроконтроллеров с ядром Cortex-M4 в значительной степени
опирается на архитектуру и систему команд ARM-процессора ARM7TDMI. Однако, в
него включены существенные дополнения. В первую очередь – это команды поддержки
операций цифровой обработки сигналов и цифрового регулирования как с числами в
формате с фиксированной точкой, так и с плавающей точкой. Наличие математического
сопроцессора позволяет во многих приложениях полностью отказаться от арифметики с
фиксированной точкой в пользу арифметики с плавающей точкой — ускорить и упростить
разработку и отладку программного обеспечения. Этим новым возможностям будет
уделена значительная часть книги.

Что представляет собой эта книга?
Учебник для студентов
Книга написана на основе многолетнего опыта преподавания курсов
«Компьютерная и микропроцессорная техника в электроприводе», «Микропроцессорные
средства в электроприводе», «Системы управления электроприводов», «Электропривод с
вентильными и шаговыми двигателями» в Национальном исследовательском
университете «МЭИ» для студентов электротехнических специальностей, а также
практического опыта создания серий цифровых систем управления электроприводов и
силовых преобразователей для отечественной промышленности.
Предыдущие учебные курсы строились на основе 8- и 16-разрядных
микроконтроллеров Intel, Motorola и 32-разрядных сигнальных микроконтроллеров Texas
Instruments. Читателям предлагается курс микропроцессорной техники на новой
элементной базе – 32-разрядных микроконтроллеров ARM с расширенными функциями
обработки сигналов в форматах с фиксированной и плавающей точкой. Процессорные
ядра ARM сегодня являются перспективными для использования во встроенных системах
управления оборудованием, о чем свидетельствует разработка и начало серийного
выпуска целого ряда отечественных микроконтроллеров на их основе.
Это учебник для студентов нескольких групп специальностей (в области
электротехники, электроэнергетики, робототехники, автоматизации и др.), в задачу
которых входит проектирование аппаратной части и программного обеспечения систем
6

ПРЕДИСЛОВИЕ
цифрового управления оборудованием (от электрического двигателя/генератора,
бензинового двигателя/дизеля, силового преобразователя энергии до электрического
транспортного средства, энергетической установки, робота, станка, производственной
автоматизированной линии).

Лабораторный практикум
Курс построен так, что теоретический материал сопровождается практической
работой читателя с отладкой проектов в интегрированной среде разработки Keil μVision с
использованием симулятора, то есть программно-логической модели процессора. Среда
μVision для разработки и отладки микропроцессорных систем на базе ARM-ядер является
общедоступной и может быть установлена на любой компьютер непосредственно с сайта
фирмы ARM после процедуры регистрации.
Для более детального освоения материала книги вам так же могут
понадобиться приложения к ней, которые распространяются бесплатно и доступны
для свободного скачивания. Ознакомиться с кратким описанием этих приложений, а
также узнать где можно скачать необходимые материалы, Вы можете в
соответствующих разделах книги: приложение 1, приложение 2.
Все примеры программ, содержащиеся в книге, в виде готовых для выполнения
проектов, представлены в приложении 2 к данной книге и могут быть немедленно
запущены и отлажены. При этом преподаватель имеет возможность формирования списка
лабораторно-практических работ с учетом специфики конкретной специальности:
например, уделить больше времени программной реализации логических контроллеров и
дискретных управляющих автоматов, или основам цифрового регулирования и цифровой
фильтрации.
Работа за компьютером сопровождается вопросами и индивидуальными заданиями,
позволяющими закрепить изучаемый материал и оценить уровень знаний. Все вопросы и
задания снабжены вариантами возможных правильных ответов и решений.

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

Справочник
Книгой можно пользоваться как справочником по архитектуре и системе команд
любых микроконтроллеров на базе процессорных ядер ARM-Cortex-M3/M4/M4F.
Приложение к книге содержит подробное описание всех имеющихся команд
процессорного ядра с графическими иллюстрациями алгоритмов выполнения наиболее
сложных, комплексных команд, ограничениями и примерами использования команд на
Ассемблере.

7

ПРЕДИСЛОВИЕ

Курс разработки программного обеспечения на Ассемблере с
использованием современных интегрированных сред разработки и отладки
В классическом варианте для описания языка Ассемблер, его директив и
особенностей разработки программ на Ассемблере, а также для описания возможностей
интегрированных сред разработки/отладки программного обеспечения должны быть
изданы отдельные книги. Мы предлагаем другой путь: постепенно, по мере изучения
системы команд, на реальных практических примерах знакомить читателя как с языком
Ассемблера, включая его директивы, так и со средствами интерактивной разработки и
отладки программ, включая возможности современных отладчиков (анализом
содержимого регистров процессора, памяти, графическим отображением динамических
процессов в логическом анализаторе и др.).
Это позволит читателю одновременно с изучением архитектуры и системы команд
процессора освоить современную технологию разработки и отладки прикладного
программного обеспечения.

Введение в микропроцессорную технику
Учебное пособие задумано и как «Введение в современную микропроцессорную
технику» на базе процессоров ARM Cortex-M3/4/4F. Поэтому, оно содержит обзор
наиболее употребительных терминов, краткие исторические сведения, сравнение разных
типов процессорных архитектур, описание используемых в процессорной технике данных,
основы двоичной арифметики и арифметики чисел с плавающей точкой. Система команд
изучается не по алфавиту, а по функциональным группам, на примерах применения в
реальных задачах. По мере освоения системы команд читатель получает необходимые
практические навыки, отлаживая законченные программные модули и используя при этом
как директивы Ассемблера, так и команды управления интегрированной средой отладки.

Перспективы
Данная книга предназначена для широкого круга читателей, поэтому специальные
вопросы, такие как особенности создания цифровых систем управления электроприводов
и источников питания, остались за кадром. Авторы планируют в ближайшее время
подготовить к изданию отдельную книгу, которая включит в себя: описание
возможностей
системных
и
специализированных
периферийных
устройств
микроконтроллеров на базе ARM-ядер Cortex-M3/M4/M4F; технику использования языка
высокого уровня C/C++ и специализированных библиотек при разработке цифровых
систем управления электрическими двигателями разных типов, управляемыми от силовых
полупроводниковых преобразователей; методику совместного применения Ассемблера и
C/C++; методы управления оборудованием в реальном времени по математическим
моделям объекта с учетом специфики аппаратной части силовых преобразователей.

Авторский состав
Книга написана: Предисловие, гл.1,2,18 - совместно Анучиным А.С. и Козаченко
В.Ф.; гл.5–6 – совместно Жарковым А.А. и Козаченко В.Ф.; гл.8 – совместно Козаченко
В.Ф. и Лашкевичем М.М.; гл.9, 22 – совместно Козаченко В.Ф., Савкиным Д.И. и Шпак
Д.М.; гл. 15 – совместно Алямкиным Д.И. и Козаченко В.Ф.; остальные главы – Козаченко
В.Ф.

8

ПРЕДИСЛОВИЕ

Рубрикация
Основной девиз книги – «От теории к практике через самостоятельную работу и
текущий контроль знаний». Читатель может проверить ход усвоения материала сам на
большом числе вопросов и заданий на самостоятельную проработку. Все вопросы и
задания снабжены ответами, рекомендациями по поиску решения или примерами
проектов, которые можно запустить и убедиться в их работоспособности.
Для удобства работы с книгой используются пиктограммы:
Теория

Выводы. Важная
информация

Контрольные
вопросы

Дополнительная
информация для
любознательных

Ответы

Комментарии и пояснения
к основному материалу

Практическая работа.
Приглашение в
лабораторию или к
самостоятельной работе
дома
Индивидуальные
практические задания с
проверкой на компьютере

Основные положения
языка Ассемблер, в том
числе директивы
Ассемблера
Техника работы в
интегрированной среде
Keil μVision5

Процессоры Cortex-M4 являются дальнейшим развитием ARM7TDMI, и при
самостоятельной работе Вы можете использовать соответствующую документацию и
технические руководства, как с сайта фирмы ARM, так и с сайтов фирм производителей
микроконтроллеров (Texas Instruments, STMicroelectronics и др.).
Авторы благодарят сотрудников кафедры Автоматизированного электропривода
НИУ «МЭИ», а также сотрудников ООО «Научно-производственная фирма ВЕКТОР» за
помощь в подготовке книги к изданию. Особая благодарность – инженерам «Научноисследовательского института электронной техники» (НИИЭТ, г. Воронеж), создавшим
первую отечественную линейку микроконтроллеров на базе ARM-Cotrex-M4F со
специализированной периферией для управления электродвигателями и силовыми
преобразователями энергии.
Все замечания и рекомендации будут с благодарностью приняты авторами:
kozachenkovf@yandex.ru, anuchin.alecksey@gmail.com, savkindmi@mpei.ru.

9

ГЛАВА 1. ПОЧЕМУ ARM

1 ПОЧЕМУ ARM?
Оглавление
1.1. Процессоры для компьютеров и встраиваемых применений .......................................... 10
1.2. Отличительные особенности компании ARM ................................................................... 11
1.3. Краткая историческая справка ............................................................................................ 13
1.4. Версии процессорных архитектур в изделиях ARM......................................................... 15
1.5. Семейство процессоров Cortex ........................................................................................... 16
1.5.1. Семейства Cortex-A и Cortex-R ................................................................................... 16
1.5.2. Семейство Cortex-M ..................................................................................................... 17
1.6. Использование ARM-ядер в отечественных микроконтроллерах ................................... 17
1.6.1. Технические характеристики микроконтроллера К1921ВК01Т .............................. 19
1.7. Преимущества архитектуры и системы команд ARM ...................................................... 21

1.1 Процессоры для компьютеров и встраиваемых
применений
Развитие и совершенствование микропроцессорной техники на протяжении
последних лет шло по двум основным направлениям: 1) процессоры для персональных
компьютеров и серверов; 2) процессоры и микроконтроллеры для встраиваемых
применений, в том числе для цифрового управления оборудованием. Конечно, эта
классификация очень условна, так как процессоры для компьютеров тоже встраиваются в
компьютеры. Эти два направления до последнего времени почти не конкурировали между
собой.
Процессоры для компьютеров были на порядки быстрее, допускали подключение
памяти большого объема, имели эффективную систему команд, адаптированную для
выполнения вычислительных операций, в том числе с плавающей точкой. Но и стоили они
от нескольких десятков до нескольких сотен долларов, что было основным препятствием
для их использования во встраиваемых применениях, вместе с большими размерами,
большим тепловыделением и необходимостью использования большого числа
дополнительных микросхем окружения. Бесспорным мировым лидером в области
процессоров для компьютеров и серверов была и остается фирма Intel.
Процессоры для встраиваемых применений, в основном микроконтроллеры,
выпускались множеством фирм-производителей (той же Intel, Texas Instruments, Motorola,
Analog Devices и т.д.). Главным требованием к ним было низкое энергопотребление и
маленькая цена - от нескольких долларов до нескольких десятков долларов.
Ситуация резко изменилась в последние два десятилетия, когда на рынке массово
появились процессоры ARM и микроконтроллеры на основе ARM-ядер. Оказалось, что
они имеют не только малое энергопотребление и малую цену, но и высокую
производительность. Это стало решающим фактором для производства на базе ARMпроцессоров телефонов, смартфонов, планшетов, иными словами – портативных
переносных компьютерных устройств, высокопроизводительных, но одновременно мало
потребляющих и дешевых.
Если до начала 2000-х годов фирма Intel практически монополизировала рынок
процессоров для компьютеров и не ощущала особой конкуренции других производителей,
10

ГЛАВА 1. ПОЧЕМУ ARM
то с появлением высокопроизводительных ARM-процессоров стала серьезно
задумываться о снижении как энергопотребления, так и стоимости своих процессоров,
чтобы не потерять не только рынок предельно дешевых микроконтроллеров для задач
встроенного управления оборудованием (что фактически уже произошло), но и рынок
портативных компьютеров, который все в большей мере заполнялся изделиями на базе
ARM-процессоров. По статистическим данным процессоры ARM сегодня используются
уже в 99% всех телефонов и смартфонов, а их доля в портативных компьютерах и
планшетах быстро растет.
Сегодня мы являемся свидетелями настоящей битвы двух ведущих процессорных
архитектур за рынок портативных компьютеров и планшетов. Наверняка, такой серьезный
производитель, как Intel, сделает все возможное, чтобы сохранить за собой не только
рынок процессоров для настольных компьютеров и серверов, но и максимально освоить
рынок процессоров для портативных компьютеров и планшетов. Появились даже
сообщения о перспективе создания фирмой Intel процессоров с совмещенной
архитектурой (x86+ARM), которые будут одинаково успешно выполнять программное
обеспечение, созданное для обеих архитектур. Производители портативных компьютеров
выпускают сегодня свои изделия как на процессорах Intel, так и на процессорах ARM,
внимательно отслеживая характеристики всех новинок. Какие процессоры окажутся
успешнее,
покажет
ближайшее
будущее,
но
это
будут
обязательно
высокопроизводительные, но, вместе с тем, малопотребляющие и дешевые процессоры.
Несколько иная ситуация сложилась в области микроконтроллеров для
встраиваемых систем управления. Быстрый рост в этом сегменте рынка ARMмикроконтроллеров уже стал свершившимся фактом. Сегодня практически все ведущие
производители микроконтроллеров, такие как Texas Instruments, Freescale, Atmel, NXP,
Samsung, LG, Sony Ericsson, nVidia и другие, выпускают свои линейки микроконтроллеров
с процессорными ядрами ARM, которые отличаются предельно низким
энергопотреблением, малой ценой и одновременно высокой производительностью.
Интересно, что и фирма Intel более 20 лет сотрудничает с фирмой ARM в области
создания новых процессорных архитектур, являясь в том числе крупнейшим владельцем
лицензированной интеллектуальной собственности ARM. Более того, в 2016 г. Intel
предложила заказчикам производить на своих заводах процессоры на базе самых
передовых ядер ARM, в том числе ядер Cortex, причем по новейшему 10 нм техпроцессу,
что должно обеспечить уникальные показатели процессоров и микроконтроллеров как по
энергопотреблению, так и по производительности. Крупнейшим заказчиком ожидается
фирма LG Electronics, производящая широкий спектр цифровых устройств разного
назначения – от планшетов и телефонов до преобразователей частоты и стиральных
машин.

1.2 Отличительные особенности компании ARM
ARM (Advanced RISC Machine – усовершенствованная RISC-машина) – это
название процессорной архитектуры и одновременно название компании, ее
разработавшей. Сама по себе RISC-архитектура (Reduced Instruction Set Computer –
компьютер с сокращенным набором команд) изначально предполагала уменьшенный
набор команд - не несколько сотен, а всего несколько десятков. По замыслу создателей,
это упрощает аппаратную часть процессора, уменьшает число транзисторов на кристалле
и, соответственно, цену и энергопотребление процессора. Однако, сложность
трансляторов и компиляторов заметно возрастает, так как в такой архитектуре именно они
выполняют основную часть работы, создавая из относительно простых команд –
комплексные, более сложные команды. Только тогда, когда были созданы качественные

11

ГЛАВА 1. ПОЧЕМУ ARM
трансляторы и компиляторы для RISC-процессоров, началось их стремительное внедрение
в промышленность. Для этого потребовалось почти два десятилетия.
Если раньше ARM-процессоры могли работать только с целочисленной
арифметикой, то сегодня они могут обрабатывать числа в формате как с фиксированной,
так и с плавающей точкой, причем с производительностью, которая еще десятилетие
назад была доступна только мощным компьютерам. Существенно расширилась и система
команд – до нескольких сотен. Но при этом сохранилось основное преимущество RISCпроцессоров: выполнение практически всех команд, в том числе таких сложных, как
обработка чисел с плавающей точкой, всего за один процессорный цикл.
Компания ARM Limited уникальна. Это единственная в мире компания, которая
занимается разработкой исключительно новых процессорных ядер и инструментария для
их
практического
использования
(трансляторов,
компиляторов,
отладчиков,
интегрированных сред разработки и т.д.), но никак не производством самих процессоров.
Не имея собственных заводов, она продает лицензии на производство ARM-процессоров
всем желающим. Фактически фирма ARM предлагает оптимизированные процессорные
ядра с детально выверенной и надежно работающей системой команд, плюс
интегрированные в процессорное ядро системные периферийные устройства, например,
такие как отладчики. На базе этих процессорных ядер любая компания, занимающаяся
производством электроники, может разработать собственный процессор или
микроконтроллер, добавляя к купленному о лицензии ядру ARM нужные объемы памяти
программ и данных, а также свою специализированную периферию.
Большинство фирм производителей используют процессорные архитектуры ARM
«как есть», не внося в архитектуру каких-либо изменений (Atmel, Samsung и т.д.). Это
создает определенную унификацию, позволяющую конечным разработчикам создавать
свои проекты на базе микроконтроллеров разных фирм, выбирая те, которые имеют
нужные объемы встроенной памяти и нужные наборы периферийных устройств, в том
числе специализированных, адаптированных к конкретной области применения
(например, к медицине, автомобильной промышленности, электромеханике и т.д.).
Мощные компании, такие как Intel, DEC, Texas Instruments, купив лицензии ARM,
создают свои собственные процессорные архитектуры и выпускают свои собственные
процессоры на их основе.
Фактически ARM продает лицензии на полноценные системы на кристалле (или
System on Chip – SoC), которые, помимо центрального процессора и сопроцессора
поддержки вычислений с плавающей точкой, могут содержать системные таймеры,
контроллеры прерываний, контроллеры памяти, порты ввода/вывода, графические
сопроцессоры, системы гео-позиционирования (GPS), модули поддержки мобильной
связи (3G, 4G), встроенные на кристалл средства интерактивной отладки программного
обеспечения. Покупатель лицензии сам определяет список модулей, который нужен
именно ему для создания своей продукции.
Такой подход очень эффективен, так как предполагает использование базовых
ядер, работающих по одной и той же системе команд с гарантированной надежностью
многократно протестированного ядра. Более того, можно использовать одно и то же,
разработанное для ARM-процессоров, системное программное обеспечение (например,
операционные системы реального времени), опирающееся только на системные ресурсы,
общие для всех производителей микроконтроллеров с одинаковыми ARM-ядрами.
Заметные преимущества получают и разработчики конечной продукции. Сокращаются
затраты на обучение инженеров-аппаратчиков и программистов. Уже накопленный опыт
быстро переносится на микроконтроллеры, имеющие то же ядро, но более высокую
производительность, расширенную память или дополнительные периферийные
устройства. Сокращается общее время от начала разработки до ее выхода на рынок, так
как появляется возможность использовать унифицированные средства разработки и
12

ГЛАВА 1. ПОЧЕМУ ARM
системное
программное
обеспечение
специализированными фирмами.

широкого

назначения,

созданное

1.3 Краткая историческая справка
1983 г. – Компания Acorn Computers, разрабатывающая портативные компьютеры
для образовательных целей, с участием Стива Фурбера и Софи Уилсона (Университет в
Манчестере), приступила к разработке нового RISC-процессора ARM1 на базе опыта
использования микропроцессоров 6502 и 68000 фирмы Motorola. В качестве изготовителя
кристалла выступала фирма Philips Semiconductor, сегодня – NXP. Первые образцы
процессоров были изготовлены в 1985 г. по технологии 3 мкм, имели на кристалле всего
25000 транзисторов, что было существенно меньше, чем в аналогичных по
производительности изделиях конкурентов. Процессор работал на тактовой частоте 4 МГц
и имел встроенный умножитель целых чисел, а также интерфейс для подключения
внешнего сопроцессора обработки чисел с плавающей точкой. Интересно, что уже в
первом ARM-процессоре поддерживались не только операции умножения, но и
умножения с накоплением, считавшиеся ранее прерогативой исключительно сигнальных
процессоров. Модифицированная версия этого процессора ARM-2 имела тактовую
частоту уже 12 МГц и выпускалась по технологии 2 мкм. На базе этих процессоров были
созданы настольные ПК «Архимед».
1989 г. – Доминирующее место в процессорах для компьютеров занимают изделия
фирм Intel (x86) и Motorola (68000). Начинается процесс интеграции на плату процессора
сопроцессоров ускорения вычислений с плавающей точкой и модулей эффективного
управления памятью. Тактовая частота поднимается до 25 МГц. Разрабатывается
процессор ARM3 с тактовой частотой 25 МГц, как альтернатива «грандам». Компания
Acorn пытается конкурировать с IBM PC на компьютерном рынке. Ее процессорами
заинтересовалась Apple для применения в своих изделиях. В конечном счете это приводит
к созданию новой компании Advanced RISC Machine (ARM) – Усовершенствованные
(продвинутые) RISC машины. Компания была создана на деньги Apple при участии
ведущих инженеров Acorn.
С этого времени кардинально изменена бизнес-модель компании: поставлена
задача – стать лидером в области разработки новых, самых перспективных процессорных
архитектур, не производить процессоры, а продавать лицензии на их производство.
Ставилась задача создавать перспективные процессорные ядра, своеобразные макроячейки, к которым фирмы производители процессоров и микроконтроллеров смогут
добавлять свою собственную дополнительную логику и свои периферийные устройства.
При этом процессорное ядро должно быть очень надежным, с тщательно отлаженной
системой команд и, конечно, малопотребляющим.
1990 г. – Компания VLSI Technology (Технология БИС) становится первым
лицензиатом, купив лицензию на производство процессора ARM6. Он незначительно
отличался от процессора ARM3, поэтому переход сразу через два номера был чисто
маркетинговым ходом. Фирма Apple использовала этот процессор (ARM610) в КПК
«Ньютон».
1993 г. – Разработан процессор ARM7 и использован в планшетах Apple с
развитыми мультимедийными возможностями. Именно с этого времени начинается
победное шествие процессоров ARM по всему миру. На его основе фирма Acorn
выпустила новую серию компьютеров и линейку КПК (карманных переносных
компьютеров).
Преемником этого процессора стал процессор ARM7TDMI, который стал
прототипом целого класса новых процессорных ядер под общим названием Cortex.
Упрощенная блок-схема процессора ARM7TDMI представлена на рис. 1.1.
13

ГЛАВА 1. ПОЧЕМУ ARM

Процессор ARM7TDMI

Порт А

Порт В

АЛУ
Шина АЛУ

Порт А

Порт В

Умножитель

Шина
умножителя

Устройство
управления
и
синхронизации

Оперативная
(данных)

Шина
данных D[31:0]

Декодер
команд

Программная
(кодовая)

Сдвиговый регистр

Блок
микропрограммного
управления

Шина В

Шина А

Шина В

Шина А

Счетчик
команд PC

Регистр
чтения
данных
Регистр
записи
данных

Шина
управления

Регистры общего назначения
(регистровый файл) r0÷ r15

Память

Регистр адреса
Шина адреса A[31:0]

Инкрементатор

Рис. 1.1 Блок-схема процессора ARM7TDMI
Отладка микропроцессорных систем является довольно трудоемким делом. Для
этой цели используются специальные аппаратные средства – отладчики (debugger) и так
называемые внутрисхемные эмуляторы (In-Circuit Emulation – ICE). Процессор
ARM7TDMI расширил возможности ARM7 с точки зрения отладки. В его состав
включена аппаратная часть, поддерживающая подключение и эффективную работу
внешних отладчиков (суффикс «D») и внутрисхемных эмуляторов (суффикс «I»).
Это был первый процессор ARM, рассчитанный на использование во встраиваемых
системах:
• Для получения более компактного кода и уменьшения объема требуемой
программной памяти был разработан новый набор 16-разрядных команд,
получивший название Thumb, который мог применяться наряду с 32-разрядными
командами ARM, требующими большего объема памяти. Эта возможность была
быстро востребована в системах связи, в том числе, в сотовых телефонах.
• Суффикс «M» в обозначении процессора означает наличие на кристалле
высокопроизводительного аппаратного умножителя (hardware multiplier),
который переводит процессор в разряд сигнальных процессоров, обеспечивая
эффективное выполнение алгоритмов цифровой обработки сигналов (DSP).
90-е годы – Фирма ARM разработала несколько еще более производительных
процессорных ядер ARM8, ARM9 и ARM10, изучение которых выходит за пределы
данного учебника. На базе этих процессорных ядер вели собственные разработки такие
лидеры компьютерной отрасли как DEC, Intel, Texas Instruments.

14

ГЛАВА 1. ПОЧЕМУ ARM
2000-е годы – Фирма ARM создала несколько линеек процессоров, среди которых
ARM11 и семейство Cortex, ряд многоядерных процессоров, а также
специализированных высоконадежных процессоров для ответственных применений.
Как мы уже отмечали, сегодня в 99% сотовых телефонов используются процессоры
ARM. Одной из первых начала их активное применение компания Nokia – один из
лидеров рынка. Большинство ведущих производителей микроконтроллеров купили
лицензии на процессорные ядра ARM и производят целые серии микроконтроллеров на
базе этих ядер.
Каждый год по всему миру изготавливаются около 10 млрд. процессоров с ARMядрами и встраиваются в различные изделия – от мобильных телефонов, фотоаппаратов,
видеокамер и планшетов до кондиционеров, роботов и автомобилей.

1.4 Версии процессорных архитектур в изделиях ARM
С точки зрения программиста определяющее значение имеет версия
процессорной архитектуры (от v4 до v8), на базе которой разработано процессорное ядро.
Таблица 1.1 Базовые типы архитектур ARM-процессоров
Архитектура

Процессоры

v4

ARM7TDMI
SC100
ARM946
ARM968
ARM926
ARM1156T2
ARM1136
ARM1176
ARM11MP
Cortex-R4
Cortex-R5
Cortex-R7
Cortex-A5
Cortex-A8
Cortex-A9
Cortex-A12
Cortex-A15
Cortex-M0
Cortex-M1
SC000
Cortex-M3
Cortex-M4
SC300
Cortex-A53
Cortex-A57

v5

v6

v7

v6-M

v7-M

v8

Разрядность

Поддерживаются наборы команд
ARM-32 bit ISA Thumb -16 bit ISA

32

˅

˅

32

˅

˅

32

˅

˅

32

˅

Thumb2

ARM

˅

˅

32

˅

32

˅

˅

64

˅

˅

˅

Именно версия процессорной архитектурыопределяет набор команд,
реализованный в процессоре. Так, процессорные ядра Cortex-M3, M4, которые будут
изучаться в данном учебнике, имеют версию архитектуры v7-M (выделена в табл. 1.1
фоном) и поддерживают комбинированный набор 32-разрядных команд ARM и 16разрядных команд Thumb, получивший название набора команд Thumb2. Это означает,
что транслятор или компилятор сам выбирает, какую из двух возможных команд
сгенерировать – 32-разрядную или 16-разрядную, позволяющую при той же выполняемой
функции в два раза уменьшить требуемый объем кода.

15

ГЛАВА 1. ПОЧЕМУ ARM
Термином «транслятор» обычно называют программу, переводящую исходный код
на языке Ассемблер конкретного процессора в машинный код. Термином «компилятор» –
программу, переводящую программу, написанную на языке высокого уровня (например,
СИ), в машинный код (возможно с созданием промежуточного файла на Ассемблере) –
подробно в главе 8.
В каждом процессорном ядре имеются дополнительные опции, например,
поддержка операций с плавающей точкой в процессорных ядрах Cortex-M4F.

1.5 Семейство процессоров Cortex
Все процессоры, ориентированные на встраиваемые применения, делятся на три
группы в зависимости от требований конечных потребителей:
• Cortex-A
• Cortex-R
• Cortex-M
Каждое семейство постоянно совершенствуется – каждый год добавляются новые
члены семейства, отличающиеся, прежде всего, производительностью и дополнительными
функциональными возможностями.
Семейство Cortex-A предназначено для встраивания в изделия, требующие
большой вычислительной мощности (тактовая частота до 3 – 4 ГГц), в том числе, в
компьютеры и устройства связи. Если простой мобильный телефон с ограниченными
функциями может быть реализован на Cortex-A5, то для планшета или компьютера
автомобиля его производительности может не хватить. Для быстрой обработки больших
объемов данных, например, в информационно-развлекательных устройствах, может
потребоваться даже мультипроцессорная система Cortex-A15 (из 4-х процессоров).
Вместе с тем, контроллер управления приводом подачи станка не требует огромной
вычислительной мощности и может быть реализован на более дешевом процессоре из
семейства Cortex-M. Именно это семейство ориентировано на микроконтроллерные
применения, связанные с управлением оборудованием в реальном времени. Интересно
отметить, что как мощные процессоры семейства Cortex-A, так и процессоры семейства
Cortex-M имеют много общего в системе адресации памяти и системе команд. Поэтому,
изучив в этой книге семейство процессоров Cortex-M, вы сможете быстро освоить и
другие более производительные процессоры.

1.5.1 Семейства Cortex-A и Cortex-R
Предназначены для высокопроизводительных устройств, требующих значительной
вычислительной мощности: портативных компьютеров, планшетов, серверов. Имеют
большую кэш-память, дополнительные арифметические блоки для поддержки графики,
сопроцессоры вычислений с плавающей точкой и модули управления памятью, которые
оптимизируют работу больших операционных систем (Windows, Linux, Android). В конце
линейки этих процессоров есть изделия, которые способны поддерживать одновременную
работу нескольких ядер (до 8). Процессоры Cortex-A5/7/8/9/12/15 являются 32разрядными, а процессоры A53 и A57 – уже 64-разрядными. Свои системы на кристалле
на базе этих ядер производят ведущие производители электроники, например, Texas
Instruments (Davinci и Sitara на базе ARM9 и Cortex-A8, OMAP на базе Cortex-A15).
Процессоры Cortex-R4/5/7 имеют архитектуру, предназначенную для работы в
очень ответственных устройствах, где надежность и предсказуемость имеет
первостепенное значение: авиация, транспорт, энергетика и т.д. Это избыточные системы,
реализованные на больше чем одном ядре. В них имеется система контроля и

16

ГЛАВА 1. ПОЧЕМУ ARM
предупреждения программного сбоя. Пример – линейка процессоров TMS570 от Texas
Instruments.

1.5.2 Семейство Cortex-M
Семейство Cortex-M представляет собой линейку процессорных ядер для
построения микроконтроллеров Cortex-M0/0+/1/3/4. Являясь 32-разрядными ядрами,
изделия на их основе призваны заменить имеющиеся на рынке 8- и 16-разрядные
микроконтроллеры. Области применения: от бытовой техники, систем управления
приводами до систем комплексной распределенной автоматизации производства. Имеют
маленькую потребляемую мощность и габариты, низкую цену. Лицензии на эти ядра
приобрели более 200 компаний, занимающихся выпуском микроконтроллеров. За счет
массового производства стоимость микроконтроллеров может находиться в диапазоне от
20 центов до 2 долларов.
В самом низу линейки процессор Cortex-M0, который имеет только 56 команд,
малые габариты и предельно низкое потребление. Подходит для реализации простых
логических контроллеров и автоматов. Реализован в линейке LPC1100 от NXP и линейке
XMC1000 от Infineon. Ядро Cortex-M0+ отличается наличием модуля защиты памяти
(MPU), перемещаемой таблицей векторов прерываний, одноцикловым интерфейсом
ввода/вывода для задач быстрого управления и дополнительной отладочной логикой.
Ядро Cortex-M1 разработано специально для реализаций на базе
программируемых логических матриц (FPGA), содержит дополнительные интерфейсные
модули памяти программ и данных, а также отладочную логику.
Процессорные ядра Cortex-M3 разработаны для приложений, требующих быстрого
отклика на внешние события, и содержат встроенный контроллер векторных
приоритетных прерываний NVIC (имеется и в младших моделях), модуль защиты памяти
MPU, модуль отладки с возможностями трассировки программы, аппаратный делитель и
одноцикловой умножитель. Процессор имеет расширенную систему команд, статическую
память SRAM и периферийный интерфейс.
Ядро Cortex-M4 имеет еще большие возможности. В него включены команды
поддержки цифровой обработки сигналов, а также (в версии Cortex-M4F) сопроцессор
поддержки вычислений с плавающей точкой. Микроконтроллеры с этими ядрами
производят многие фирмы, среди них Atmel, Freescale, Texas Instruments, в том числе и
отечественные компании.

1.6 Использование ARM-ядер в отечественных
микроконтроллерах
В последние несколько лет в России активно работает программа импортозамещения, в рамках которой большое внимание уделяется созданию отечественных
процессоров и микроконтроллеров, не уступающих по своим характеристикам лучшим
западным аналогам. Возможный путь решения проблемы: покупка лицензий на базовые
ARM-ядра у фирмы ARM и производство на этой основе микроконтроллеров с памятью и
периферией собственной разработки. При этом объемы и типы встроенной памяти, а
также встроенная периферия могут создаваться самостоятельно и адаптироваться для
поддержки важнейших отраслей отечественного производства.
Почему бы не взять у ARM все самое лучшее, надежное и многократно поверенное,
как это делают, не стесняясь, даже такие известные фирмы как Intel и Texas Instruments:
одну из самых перспективных сегодня процессорных архитектур, поддержанную мощной
системой команд и целым рядом встроенных в ядро системных периферийных устройств.
При этом периферия будет оптимизироваться самостоятельно под потребности основных
17

ГЛАВА 1. ПОЧЕМУ ARM
российских потребителей микроконтроллеров. Преимуществом такого подхода является и
то, что становится доступен весь арсенал специализированного программного
обеспечения, уже разработанный в мире, в том числе интегрированные среды разработки
и отладки программного обеспечения, специализированные библиотеки, драйверы
типовых интерфейсов, операционные системы.
Приведем несколько примеров. Одна из ведущих отечественных электронных
компаний АО «ПКК Миландр», г. Зеленоград, разработала, освоила в производстве и уже
поставляет потребителям 32-разрядные микроконтроллеры 1986ВЕ9х с ядром на базе
ARM Cortex-M3 для широких областей применения взамен любых 8- и 16-разрядных
микроконтроллеров импортного производства. В состав периферийных устройств
включены три таймера с функциями генерации ШИМ-сигналов, что позволяет
использовать микроконтроллер для создания относительно несложных, но массовых
преобразователей частоты для управления асинхронными двигателями, например, в
приводах насосов и вентиляторов ЖКХ. Вычислительных ресурсов микроконтроллера и
встроенных коммуникационных интерфейсов достаточно для решения большинства
практических задач (два CAN контроллера, контроллеры UART, SPI, I2C и USB).
Интерес для разработчиков представляют и новейшие микроконтроллеры
1986ВЕ8Т этой фирмы, в основу которых заложено более производительное ядро ARMCortex-M4F c модулем аппаратной поддержки вычислений с числами в формате с
плавающей точкой однократной точности. Они предназначены для создания сложных,
высоко производительных информационно-измерительных систем с большим числом
аналоговых входов и выходов. О существенно возросших возможностях российских
производителей электроники говорит набор интерфейсов, интегрированных на кристалл
(от контроллеров Ethernet и USB до специализированных интерфейсов, ориентированных
на преимущественное применение в авиационной технике – ARINC, SpaceWire).
Микроконтроллеры имеют также ряд встроенных систем контроля, обнаружения и
исправления ошибок, в том числе при доступе к памяти и периферии, что позволяет
использовать их в особо ответственных применениях.
В 2016 г. АО «НИИЭТ», г. Воронеж, завершил разработку и выпустил опытнопромышленную партию первых отечественных специализированных микроконтроллеров
К1921ВК01Т типа «Motor Control» (Управление двигателями), «Motion Control»
(Управление движениями) и «Power Control» (Управление силовыми преобразователями
энергии). У отечественных специалистов, работающих в области машиностроения,
энергетики, транспорта, электромеханики, наконец-то появилась отечественная
элементная база на уровне лучших западных решений.

18

ГЛАВА 1. ПОЧЕМУ ARM

1.6.1 Технические характеристики микроконтроллера К1921ВК01Т

Рис. 1.2 Структурная схема отечественного микроконтроллера К1921ВК01Т фирмы АО
«НИИЭТ»
В качестве ядра используется одно из самых перспективных микропроцессорных
ядер ARM-Cortex-M4F со встроенным модулем поддержки вычислений с плавающей
точкой FPU, модулем защиты памяти MPU, контроллером вложенных векторных
прерываний NVIC и набором модулей для поддержки интерактивной отладки с выходом
на два стандартных отладочных интерфейса JTAG и SWD. Процессор обеспечивает
производительность до 125 MIPS (миллионов инструкций в секунду). В ближайшее время
планируется поднять производительность микроконтроллера до 200 MIPS. Основные
технические данные:
• Встроенная FLASH-память программ емкостью 1 Мбайт;
• Встроенное статическое ОЗУ (RAM) емкостью 192 Кбайта;
• Дополнительная FLASH-память данных объёмом 64 Кбайта;
• Возможность расширения памяти - контроллер внешней памяти (SRAM, PROM, NOR
Flash);
• 32-канальный контроллер прямого доступа к памяти (DMA);
• Система сброса и сторожевой таймер (Watchdog);
• Часы реального времени (Real Time Clock) с батарейным питанием;
• Синтезатор таковой частоты микроконтроллера (PLL);
• Двенадцать 2-канальных 12-разрядных АЦП с временем выборки 2 MВыб/с, с
режимами цифрового компаратора для каждого из каналов (равно или больше, равно
или меньше, попадание в диапазон, выход из диапазона), с функцией автоматического
запуска модулей ШИМ-генератора по событию в АЦП (окончанию преобразования), с
дополнительным встроенным датчиком температуры (T-Sensor) для контроля
температурного режима собственно микроконтроллера;
19

ГЛАВА 1. ПОЧЕМУ ARM
















Восемнадцать модулей ШИМ (PWM) или девять двухканальных модулей с
комплиментарными выходами, из которых шесть могут работать в режиме «высокого»
разрешения (HRPWM) с возможностью прецизионного изменения длительности
импульсов на величину меньшую периода тактового сигнала процессора;
Шесть модулей захвата/сравнения (CAP);
Три аналоговых компаратора с функцией автоматического запуска модулей ШИМ по
событию сравнения (равно или больше, равно или меньше);
Три 32-битных таймера общего назначения;
Два порта CAN 2.0b;
Два интерфейса I2C с поддержкой режима High Speed (более 1 МГц);
Два квадратурных декодера (enhanced Quadrature Encoded Pulse – eQEP) для обработки
сигналов импульсных датчиков положения ротора и исполнительного органа в
высокопроизводительных системах позиционирования и слежения (определение
положения, направления и скорости вращения);
Четыре порта синхронного периферийного интерфейса SPI;
Четыре модуля коммуникационного интерфейса SCI (UART);
Интерфейс USB 2.0 Device /Host с физическим уровнем PHY;
Интерфейс Ethernet 10/100 Мбит/с с интерфейсом MII;
Отладочный интерфейс (JTAG и ARM Serial Wire Debug (SWD));
Не менее 88 выводов портов ввода/вывода общего назначения (GPIO), раздельно
программируемых и мультиплексированных со спецфункциями периферийных
устройств.

Встроенная периферия этого микроконтроллера по всем показателям не уступает
самым лучшим западным образцам специализированных микроконтроллеров, например,
периферии микроконтроллера TMS320F28335 фирмы Texas Instruments, который, по
мнению специалистов, долгое время являлся лучшим в мире сигнальным
микроконтроллером типа «Motor Control». По вычислительной производительности
отечественный микроконтроллер пока несколько уступает аналогам (100 MГц вместо 150
МГц). Но уже сейчас его возможностей более чем достаточно для решения большинства
практических задач в области отечественной энергетики, электромеханики, транспорта и
комплексной автоматизации производства. При частоте 100 МГц гарантируется
производительность не ниже 125 MIPS. А в конце 2017 года должна появиться
модификация микроконтроллера с частотой 200 МГц, что позволит приблизиться к
новейшим микроконтроллерам TMS320F28377 фирмы Texas Instruments.
В распоряжении отечественных разработчиков сегодня имеется
большое число как импортных, так и отечественных микроконтроллеров на
базе процессорных ядер ARM-Cortex-M3/M4/M4F, отличающихся высокой
производительностью (тактовой частотой), объемами встроенной памяти
программ и данных, наборами встроенных периферийных устройств и низкой
ценой. Задача освоения этой новой и перспективной техники российскими специалистами
и студентами является чрезвычайно актуальной. Это будет способствовать быстрому
возрождению отечественного производства на принципиально новой цифровой
элементной базе – созданию современного цифрового производства.

20

ГЛАВА 1. ПОЧЕМУ ARM

1.7 Преимущества архитектуры и системы команд ARM
Ниже перечислены важнейшие преимущества архитектуры и системы команд
процессоров ARM, которые обеспечили завоевание рынка микроконтроллерных
применений этими процессорами:
1) Обработка данных (арифметические, логические и другие операции), находящихся
исключительно в сверхоперативной памяти центрального процессора (ЦПУ) –
регистрах общего назначения с использованием самой компактной и быстрой
регистровой адресации операндов. Обработка данных в формате с плавающей точкой
исключительно в регистрах окружения сопроцессора. Полное отсутствие команд
обработки, операнды которых расположены в памяти – уменьшение длины всех
команд за счет исключения полей адресации операндов в памяти. Унификация
форматов команд до 32- и 16-разрядных команд.
2) Ускорение выполнения операций за счет минимизации числа обращений к памяти,
которая существенно медленнее, чем регистровая память центрального процессора.
Быстрое выполнение большинства команд, в том числе команд сопроцессора, за один
цикл: одноцикловое выполнение команд – визитная карточка процессоров с RISCархитектурой. Даже такие комплексные команды, как умножение с накоплением чисел
в формате с плавающей точкой, выполняются за один цикл, и только некоторые
команды требуют большего числа циклов (например, деление, извлечение квадратного
корня). Как следствие – высокая вычислительная производительность процессорных
ядер.
3) Организация работы с памятью с помощью отдельных инструкций загрузки/
сохранения содержимого регистров из памяти/в память. Применение для этого
исключительно косвенной адресации операндов с использованием в качестве
регистров указателей любых регистров ЦПУ.
4) Развитые средства стековой адресации и относительной адресации данных в памяти
по содержимому счетчика команд. Возможность одной командой за один цикл
выполнить загрузку/сохранение любых 32-разрядных данных из памяти/в память.
5) Временное совмещение ряда операций, таких как считывание кодов команд на
конвейер команд и считывание данных из кодовой памяти при выполнении операций
загрузки данных в регистры ЦПУ.
6) Использование самых эффективных методов адресации массивов данных в памяти,
включая базовую адресацию со смещением, адресацию с пред- и пост-смещением
указателя (авто-инкрементирование/декрементирование до или после доступа к
данным), базовую адресацию с автоматическим вычислением смещения относительно
базы в зависимости от длины данных в памяти (байт, полуслово, слово) и индекса.
Поддержка эффективного доступа к любым структурам данных.
7) Использование прерываемых команд множественной загрузки/сохранения регистров
ЦПУ или сопроцессора из памяти/в память.
8) Отсутствие регистра-аккумулятора, предназначенного для хранения промежуточных
результатов вычислений, и использование для этой цели любого из регистров
процессора или сопроцессора (при вычислениях с плавающей точкой).
9) Поддержка не выровненного по длине 32-разрядного слова доступа к памяти для
экономии объема памяти при работе с байтами и полусловами.
10) Включение в архитектуру центрального процессора, кроме АЛУ и аппаратных
умножителя и делителя, кольцевого сдвигового регистра, позволяющего выполнять
21

ГЛАВА 1. ПОЧЕМУ ARM
попутные операции масштабирования одного из операндов при сохранении времени
выполнения команды всего за один цикл.
11) Формирование флагов результатов операций только тогда, когда это необходимо
(опциональный суффикс установки флагов). Наличие команд сравнения операндов в
формате с плавающей точкой.
12) Возможность условного выполнения всех команд процессора и сопроцессора по
состоянию суффиксов условного выполнения, единых для ЦПУ и сопроцессора.
Поддержка методов линейного модульного программирования, в том числе, на
Ассемблере, практически без использования команд условного ветвления.
Наглядность, простота отладки и сопровождения программных продуктов.
13) Наличие «семафорной памяти» с побитовой адресацией и неявно выраженного
«битового сопроцессора».
14) Наличие команд преобразования форматов данных, существенно облегчающих
интерфейс ввода переменных и вывода управляющих воздействий.
15) Программная поддержка операций цифровой обработки сигналов с числами как в
формате с фиксированной, так и с плавающей точкой. Поддержка организации в
памяти кольцевых буферов выборок данных.
16) Интеллектуальный транслятор с автоматической генерацией оптимальных команд
вместо псевдокоманд Ассемблера. Автоматическая генерация транслятором блоков
условного выполнения с числом условно выполняемых команд в блоке – до четырех.

Список рекомендуемой литературы
1) Техническая документация на микроконтроллер 1921ВК01Т1 фирмы АО
«НИИЭТ». Доступно по ссылке.
2) Джозеф Ю. Ядро Cortex-M3 компании ARM. Полное руководство/ Ю. Джозеф; пер.
с англ. А.В. Евстафеева. – М.: Додэка-XXI, 2012. – 552 c. ил. (Мировая
электроника).
3) Fisher M. ARM Cortex M4 Cookbook. – Packt Publishing Ltd. – 2016.
4) ARM DDI 0403C_errata_v3, ARMv7-M Architecture Reference Manual, Arm Limited,
2010.
5) Texas Instruments. SPMU 159a. Cortex-M3/M4F Instruction Set. Technical User’s
Manual. Texas Instruments Inc. – 2011.
6) Электронный ресурс https://www.arm.com

22

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ

2 ВВЕДЕНИЕ В
МИКРОПРОЦЕССОРНУЮ
ТЕХНИКУ
Оглавление
2.1. Цифровой мир ....................................................................................................................... 23
2.2. Структура микропроцессорной системы. Основные понятия ......................................... 24
2.3. Общие принципы работы процессора ................................................................................ 27
2.4. Что такое Ассемблер? .......................................................................................................... 29
2.5. Системы счисления, используемые в процессорной технике .......................................... 31
2.6. Сопроцессоры ....................................................................................................................... 33
2.7. Прерывания и исключения ................................................................................................. 34
2.7.1. Прерывания ................................................................................................................... 34
2.7.2. Исключения ................................................................................................................... 36

2.1 Цифровой мир
Компьютерная техника прочно вошла в нашу жизнь. Мы уже не
мыслим свое существование без мобильного телефона, планшета или
компьютера и порой не задумываемся о том, сколько усилий инженеров и
программистов потребовалось для этого. Современный мир понастоящему стал «цифровым», причем это касается не только бытовых
устройств, но и промышленного оборудования. Сегодня каждое
устройство от кондиционера до станка с числовым программным управлением и робота
имеет по крайней мере один процессор, управляющий его работой. Процессоры стали
непременным атрибутом любого оборудования.
Типичный пример – «умный дом», в котором каждая система (водоснабжения,
кондиционирования воздуха, освещения, обогрева, охраны и т.д.), имея свой собственный
процессор, объединена в общую локальную сеть, доступ к которой выполняется
непосредственно со смартфона хозяина. На создание такой техники ушло почти полвека
усилий инженеров и программистов. Современный планшет, например, представляет
собой не только полноценный компьютер, но и мобильный телефон, имеет все
необходимые интерфейсы для подключения к Интернету и периферийным устройствам
(USB, Wi-Fi, Bluetooth). Более того, он становится центром контроля и управления как
местными, так удаленными интеллектуальными устройствами, находящимися в
распоряжении хозяина (от медиацентра до системы охраны дачи).
Инженер любой специальности сегодня должен обладать знаниями в области
микропроцессорной техники, достаточными для разработки, отладки и сопровождения
цифровой системы управления объектами и оборудованием в своей предметной области.
Эта глава содержит необходимые начальные сведения, которые можно считать общими,
не зависящими от типа используемого процессора или микроконтроллера. Тем не менее,

23

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
обратите внимание на некоторые попутные замечания, которые касаются основной темы
книги – процессорных ядер ARM Cortex-M.

2.2 Структура микропроцессорной системы. Основные
понятия
Базовым элементом любой цифровой системы (вычислительной или управляющей)
является процессор – устройство для обработки информации и управления процессом
обработки. Процессор может быть выполнен на одной или нескольких печатных платах,
как это было еще полвека назад в первых компьютерах, а также в виде кристалла большой
интегральной схемы (БИС), как сейчас. В последнем случае он называется
микропроцессором (МП).
Процессор – это программно-управляемое устройство, которое выполняет все
действия в строгом соответствии с программой, находящейся в памяти (рис. 2.1).
Программа, которая может быть выполнена процессором, называется исполняемой
программой или программой в машинных кодах (в двоичном коде).

ПРОЦЕССОР (ЦПУ)

Блок
микропрограммного
управления

ПАМЯТЬ

Шина данных
Регистр
команд

Кодовая память
(Память программ)

Дешифратор
команд

• Программа на
машинном языке
• Константы и
таблицы констант

Шина адреса

Операционный блок

Память данных
(оперативная)
Переменные

Счетчик
команд PC
Регистры общего
назначения

УВВ
(Периферийные
устройства)

Периферийные устройства,
встроенные в ЦПУ

Рис. 2.1 Структура микропроцессорной системы
Процессор и память являются обязательными компонентами любой
микропроцессорной системы (МПС). Для того, чтобы действия, выполняемые
процессором, были «видны» пользователю или как-то воздействовали на объект
управления, в состав микропроцессорной системы должен входить еще один
обязательный компонент – устройства ввода/вывода (УВВ) или периферийные
устройства (ПУ). В простейшем случае это могут быть просто порты ввода/вывода
24

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
данных. В более сложном – необходимы специальные каналы связи (интерфейсы) и
специальные периферийные устройства. Так, ввести информацию о состоянии
управляемого процесса можно только сняв показания с аналоговых датчиков (давления,
расхода, температуры, тока, напряжения и т.д.) и преобразовав ее с помощью аналогоцифрового преобразователя (АЦП), в цифровую форму (цифровой код). Если объект
управления (например, управляемый источник питания) имеет исполнительное
устройство с аналоговым входом, то потребуется преобразование рассчитанного в
цифровой системе управляющего воздействия в аналоговую форму – цифро-аналоговый
преобразователь (ЦАП).
В последнее время получила развитие концепция прямого цифрового управления
всеми компонентами оборудования, в соответствии с которой в состав МПС должны
входить специализированные периферийные устройства, которые могут работать
автономно, минимально используя ресурсы процессора и памяти: многоканальные
ШИМ-генераторы, модули захвата и сравнения, квадратурные декодеры, ЦАП, АЦП и т.д.
Все компоненты микропроцессорной системы располагаются на одной или
нескольких печатных платах – printed circuit board (PCB), соединенных между собой
«межплатными» интерфейсами. В состав МПС входит собственно процессор, кодовая
память или память программ (ПЗУ – постоянное запоминающее устройство) с
доступом к данным только по чтению, содержащая также константы и таблицы
констант, оперативная память (ОЗУ) или память данных, содержащая переменные, с
доступом по записи и по чтению, а также периферийные устройства. На печатной
плате устанавливаются и коммуникационные интерфейсы для сопряжения МПС с
другими системами управления более высокого уровня. Они поддерживаются
специальными интерфейсными БИС – контроллерами интерфейсов.
Центральным процессорным устройством (ЦПУ) называется основной
процессор в системе, который, как правило, работает с целыми числами и числами в
формате с фиксированной точкой. Если необходима поддержка вычислений с плавающей
точкой – она реализуется либо с помощью специальных библиотек, либо подключением
сопроцессора вычислений с плавающей точкой. Сопроцессор – это отдельное устройство
или опция производителя микроконтроллера, которая добавляется на кристалл по
желанию заказчика и существенно расширяет возможности ЦПУ.
Некоторые, наиболее важные периферийные устройства, такие как системный
таймер и контроллер прерываний, могут непосредственно встраиваться в ЦПУ. Так, в
частности, поступает фирма ARM со своими процессорными ядрами (рис. 2.1). При таком
подходе пользователи получают ряд преимуществ: достигается унификация процессорных
ядер независимо от их производительности – при переходе на более производительное
ядро наработки в области программного обеспечения (например, созданные ранее
операционные системы реального времени – ОСРВ) сохраняются; так как системная
периферия интегрирована в процессорное ядро, ее производительность уже
оптимизирована разработчиком процессорного ядра (фирмой ARM).
Совокупность процессора, памяти, одного или нескольких сопроцессоров и
периферийных устройств, размещенных на одном кристалле, в одном корпусе БИС,
называется микроконтроллером или системой на кристалле system-on-chips (SoC).
Системы на кристалле экономят место на плате и существенно уменьшают
энергопотребление, что особенно важно для мобильных устройств с батарейным
питанием. Современные системы на кристалле становятся все более сложными, объединяя
порой процессоры разных архитектур для получения максимальных преимуществ каждой
из них. Так, в двухъядерных микроконтроллерах Texas Instruments «Concerto», ARM-ядро
выполняет коммуникационные функции (обмена данными по типовым интерфейсам), а
функции управления в реальном времени двигателями и силовыми преобразователями –

25

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
ядро специализированного сигнального микроконтроллера С2000 фирмы Texas
Instruments, оптимизированное для решения подобных задач.
Системы на кристалле – одна из главных тенденций развития современной
процессорной техники. Они позволяют использовать освоенные ранее, надежные и
качественные аппаратные и программные решения в новых разработках, ускоряя процесс
создания все более производительных и функциональных устройств, не начиная каждый
раз процесс разработки «с нуля». Мы не оговорились, эта тенденция касается и
программных продуктов. Так, создать драйвер для поддержки интерфейса USB или
Ethernet очень нелегкая работа, которая может потребовать нескольких «человеко-лет»
труда высококвалифицированных программистов. Проще купить этот продукт,
использовать его и быстрее выйти на рынок со своим новым изделием. Зачастую
разработчики микроконтроллеров, заинтересованные в росте продаж собственных
кристаллов, делают подобные драйверы или даже целые библиотеки специализированных
подпрограмм общедоступными. Это на руку разработчикам конечного оборудования, так
как резко сокращает время от начала разработки до выхода изделия на рынок и,
естественно, стоимость разработки.
Процессорная техника очень быстро совершенствуется. Производительность
процессоров удваивается каждые два года. Это явление даже получило название «Закон
Мура». Поэтому, важно сохранить сделанные ранее вложения в разработку. Это возможно
только в том случае, когда сопряжение разных устройств реализуется по стандартным
протоколам обмена и интерфейсам. Они разработаны для множества изделий, в том
числе для сервоприводов станков и роботов. Если Вы создали инвертор для сервопривода
со встроенной цифровой системой управления, но не совместимый со стандартом, то его
вряд ли кто-то купит. Вы не сможете подключить его ни к одной системе управления
верхнего уровня (системе числового программного управления ЧПУ или промышленному
программируемому контроллеру ПК).
Под системой управления нижнего уровня обычно понимается встроенная
система управления, которая интегрирована в оборудование (преобразователь частоты,
робот, кондиционер, станок и т.д.), а под системой управления верхнего уровня –
промышленный компьютер, промышленный программируемый контроллер, система
ЧПУ, к которой по типовым интерфейсам подключаются встроенные системы управления
нижнего уровня. Они, как правило, объединяются в локальную промышленную сеть,
образуя одну общую систему управления, в которой может быть несколько иерархий
управления. Такая система управления может быть распределенной с целым рядом
контроллеров нижнего уровня, встроенных в оборудование, контроллеров среднего и
верхнего уровня. Это может быть система автоматического управления конвейерной
или сборочной линией, производственным участком, электромобилем и т.д.
За последние годы для каждой области техники разработчики микроконтроллеров
создали целый ряд специализированных периферийных устройств, оптимизированных
по режимам работы, выполняемым функциям и производительности именно к этой
предметной области. Создана такая техника и для управления двигателями (Motor
Control), системами питания (Power Control), автомобильной, авиационной
электроникой и др. Такие микроконтроллеры или системы на кристалле получили
название специализированных микроконтроллеров. Целый ряд всемирно известных
производителей, таких как Texas Instruments, ST Microelectronics и др., производят
специализированные микроконтроллеры и на базе ядер ARM Cortex-M3/M4/M4F,
изучению которых посвящена эта книга.
Выпускаются целые серии микроконтроллеров, которые имеют общий
центральный процессор, но разные объемы памяти и наборы периферийных устройств
(ПУ) на кристалле. Разработчики систем управления нижнего уровня имеют возможность
выбора микроконтроллера с нужной тактовой частотой (производительностью), объемами
26

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
памяти программ и данных, коммуникационными интерфейсами и встроенными
периферийными устройствами, что позволяет оптимизировать конкретную систему
управления по стоимости и потребляемой энергии.
Микроконтроллеры совершили революцию в современном управлении
оборудованием – они встраиваются непосредственно в изделие (в
стиральные машины, холодильники, кондиционеры, двигатели автомобилей и
т.д.), обеспечивая прямое цифровое управление каждым элементом
оборудования, вплоть до отдельного силового ключа, а также прямой
интерфейс с датчиками аналоговых, дискретных и импульсных сигналов. Широкий набор
встроенных интерфейсов обеспечивает сопряжение и с системами управления более
высокого уровня, и с человеком-оператором.

2.3 Общие принципы работы процессора
Процессор, память и периферийные устройства объединяются между собой
системой шин (см. рис. 2.1). Различают шину адреса, по которой процессор информирует
внешнюю память о том, к какой конкретно ячейке памяти последует обращение, а также
шину данных, по которой, собственно, и пересылаются данные. Шина адреса
однонаправленная – от процессора к памяти и периферии, а шина данных –
двунаправленная.
Шина управления (не показана на рис. 2.1) предназначена, как минимум, для
информирования внешней памяти или периферийного устройства о направлении передачи
информации – чтение или запись. В любом случае, инициатором обмена данными с
памятью или периферийным устройством является процессор. Именно он сначала выдает
адрес ячейки памяти или регистра периферийного устройства, затем сигнал направления
передачи данных и только потом получает с шины данных или выдает на нее нужную
информацию.
Шины адреса и данных могут быть отдельными (изолированными) или
совмещенными (мультиплексированными). Это означает, что часть времени шина
используется в качестве шины адреса, а часть – в качестве шины данных. При этом в
начале каждого цикла обращения к памяти или периферии процессор сначала выдает
адрес объекта доступа, который фиксируется в микропроцессорной системе (вне
процессора), а затем та же шина переключается на использование в качестве шины
данных.
Устройства ввода/вывода (периферийные устройства), как правило, являются
программируемыми и содержат внутри себя целый набор регистров. Каждый из них
имеет свой персональный адрес. В процессорах ARM реализована технология
отображения адресов регистров периферийных устройств на память. Это означает,
что все регистры ПУ представляют собой как бы ячейки памяти, доступ к которым может
производиться точно так же, как к обычным ячейкам памяти, с использованием всего
мощного арсенала команд процессора. Эта технология имеет некоторые недостатки –
часть общего адресного пространства приходится отводить под регистры периферийных
устройств.
Альтернативное решение, которое часто применялось на заре процессорной
техники – так называемый, изолированный ввод/вывод, когда для работы с устройствами
ввода/вывода используются отдельные шины адреса и данных. При этом не «расходуется»
адресное пространство памяти, но для доступа к УВВ нужны специальные команды
ввода/вывода типа Input (Ввод) и Output (Вывод).
Что определяет разрядность шины адреса? Это объем прямо адресуемой
процессором памяти. Так, если шина адреса 16-разрядная, как это было в первых
27

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
процессорах и микроконтроллерах, то объем доступной процессору памяти составит всего
2n=216=65536=64×1024
=
64
K
ячеек,
если
32-разрядная

то
уже
32
2 =4,294,967,296=4,096×1,048,576 = 4096 М ячеек = 4×1,073,741,824 = 4 Г ячеек памяти.
Здесь и далее в книге для удобства читателей мы будем
использовать запятую не в качестве десятичной точки, а в качестве
разделителя цифр, чтобы длинные числа были более «читабельными».
Десятичная точка будет использоваться для разделения целой и дробной
части вещественного числа, как обычно.
В отличие от физики, где соответствующие весовые коэффициенты (К, М, Г)
определяются степенью числа 10, в процессорной технике они определяются степенью
числа 2, как основания двоичной системы счисления – табл. 2.1.
Это более удобно, так как адресная шина любой БИС памяти может иметь только
целое число двоичных разрядов. Так, если она 10-разрядная, то может содержать только
210=1024 К ячеек, если 32-разрядная, как у процессоров ARM, – то 4 Г ячеек.
Таблица 2.1 Весовые коэффициенты в физике и процессорной технике
Весовой коэффициент

В физике
Степень 10

К - Кило
М - Мега
Г - Гига

3

10
106
109

В процессорной технике
Значение
1000
1,000,000
1,000,000,000

Степень 2
10

2
220
230

Значение
1024
1,048,576
1,073,741,824

Обычно разрядность ячейки памяти – байт (8 двоичных разрядов). Это означает,
что процессоры ARM могут адресовать 4 Г байта памяти.
В большинстве ранее выпускаемых процессоров и микроконтроллеров разрядность
шины данных совпадала с разрядностью памяти. В соответствии с этим различают 8-, 16и 32-разрядные процессоры. Они обрабатывают за один цикл доступа к памяти либо
байт, либо 16-разрядное полуслово, либо 32-разрядное слово.
В процессорах АRM шина данных 32-разрядная. Это означает, что фактически
доступ к памяти 32-разрядный. Однако, внутри процессора возможна обработка не только
полного 32-разрядного слова, но и полуслова и даже байта. В этом случае из памяти
всегда считывается полное слово, но «лишняя» информация внутри процессора как бы
отбрасывается. Таким образом, процессоры ARM, будучи настоящими 32-разрядными
процессорами, допускают работу с данными и в более коротких форматах, присущих 8-и
и 16-разрядным процессорам.
Кто управляет порядком выборки команд из памяти? В соответствии с концепцией,
предложенной одним из основоположников компьютеров фон-Нейманом, за это отвечает
специальный регистр внутри процессора, называемый счетчиком команд или
программным счетчиком PC (Program Counter) (см. рис. 2.1). Он всегда содержит адрес
команды, подлежащей выполнению. Когда очередная команда считывается из памяти и
дешифрируется, в зависимости от ее длины счетчик команд автоматически
инкрементируется нужное число раз. Так, если команда 32-разрядная и занимает в памяти
4 байта, то счетчик команд получает приращение +4. Счетчик команд является
указателем адреса очередной команды, подлежащей выполнению. Его содержимое
выставляется на шину адреса при каждом доступе процессора к очередной команде (пока
для простоты предполагается, что в процессоре нет конвейера команд).
При сбросе процессора или при подаче на него питания содержимое счетчика
команд PC автоматически устанавливается в состояние, соответствующее начальному
адресу кодовой памяти. Именно с этого адреса начинается работа процессора, как
28

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
программно-управляемого устройства (в следующих главах этот простой механизм будет
уточнен для процессоров ARM).
Считывая первые данные из памяти, процессор по умолчанию считает, что это –
инструкция. Она поступает в специальный регистр, называемый регистром команд и
расшифровывается. Так как в кодовой памяти могут находиться не только команды, но и
данные (константы и таблицы констант), их выборка будет определяться только
соответствующей командой процессора, в процессе ее выполнения. Применительно к
ARM это может быть, например, команда загрузки данных из памяти с относительной
адресацией по содержимому счетчика команд (текущему).
Каждый процессор имеет свой собственный набор команд, то есть совокупность
всех инструкций, которые могут быть выполнены процессором. Первые ARM процессоры
имели всего несколько десятков команд, современные – более двух сотен.
Каждая команда из набора команд имеет свой персональный код, называемый
кодом операции. По этому коду процессор определяет, что именно он должен делать.
Функцию
дешифрации
команды
выполняет
устройство,
называемое
дешифратором команд. Дешифратор команд по коду операции запускает
соответствующий данной команде дискретный управляющий автомат, задающий
последовательность микроопераций, приводящих к выполнению команды.
Выполнение любой команды в процессоре разбивается на ряд микроопераций, то
есть минимально-возможных (атомарных для данного процессора) действий. Каждой
команде соответствует своя собственная последовательность микроопераций. Если
операция простая, то таких микроопераций мало, если сложная – много. Все
микрооперации кодируются с помощью специального микрокода. По существу, для
каждой операции внутри процессора создается своя микропрограмма выполнения
команды, которая запускается после расшифровки кода операции.
В составе любого процессора имеется так называемый операционный блок,
который выполняет арифметические и логические операции (сложение, вычитание и т.д.).
Его ядром является двухпортовое арифметико-логическое устройство (AЛУ). Каждый
из портов АЛУ принимает по операнду, который, в соответствии с кодом операции, может
поступать либо из регистров общего назначения (РОН) – сверхоперативной памяти
процессора, либо из памяти. В процессорах ARM в качестве операндов используются
исключительно регистры ЦПУ. Как только операнды загружены, AЛУ получает команду
на выполнение нужного действия. Результат сохраняется либо в специальном регистре –
аккумуляторе, либо в любом из регистров общего назначения (как в процессорах ARM).
В составе процессора, как минимум, имеются: регистр команд,
принимающий очередную команду из памяти на расшифровку; счетчик
команд, следящий за порядком выборки команд из памяти; операционный
блок, выполняющий предусмотренные системой команд процессора
операции; регистры общего назначения, содержащие исходные операнды и
принимающие результат операции; устройство управления и синхронизации,
обеспечивающее выполнение всех операций посинхросигналам от тактового генератора;
система шин для подключения памяти и периферийных устройств.

2.4 Что такое Ассемблер?
Исполняемая программа на машинном языке представляет собой
последовательность цифровых двоичных кодов определенной разрядности, так как любая
информация в цифровых системах хранится и обрабатывается только в двоичном коде.
Написать такую программу – непростая задача. Поэтому разработчики процессоров дают
каждой команде из набора команд некоторое символическое имя – мнемокод, который,
29

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
как правило, является отражением действия, выполняемого командой. Например, «MOV»
– от англ. move – переместить, «ADD» - от англ. add – сложить и т.д. Программа,
написанная на языке мнемокодов конкретного процессора, называется программой на
языке Ассемблер.
Так, строка программы на ассемблере (для процессоров ARM)
MOV r0, r1
означает: переслать данные из регистра-источника r1 в регистр-приемник r0.
Таким образом, каждая команда из набора команд процессора имеет свой
персональный мнемокод и свой собственный синтаксис, который определяет, что
может быть указано в поле операндов этой команды и в какой форме. Например,
возможна команда пересылки данных
MOV r0, #1
в которой выполняется загрузка непосредственными данными регистра r0, то есть его
инициализация константой 1. Непосредственными называются данные, значения
которых указаны в самой команде – они присутствуют в формате команды вместе с кодом
операции.
Для каждого типа процессора разработчиками процессора создается свой
собственный язык: Ассемблер конкретного процессора – язык его мнемокодов команд,
отражающий архитектуру процессора, его систему команд, состав и символические имена
встроенных в процессор регистров общего назначения. Так, один из ассемблеров,
созданный для программирования процессоров ARM, называется ARM ASM. Для одного
и того же процессора разными фирмами может быть создано несколько языков
ассемблера, отличающихся по своим возможностям (обычно, по составу и функциям
входящих в язык псевдокоманд – директив). Далее в тексте книги слово «Ассемблер»,
набранное с заглавной буквы, будет обозначать конкретную версию языка ассемблер, а
набранное с маленькой буквы – любой язык мнемокодов процессора.
Регистры общего назначения образуют сверхоперативную память процессора, то
есть являются принадлежностью процессора, в отличие от памяти программ, которая
является внешней. Доступ к регистрам общего назначения предельно быстрый. Эти
регистры могут использоваться в качестве операндов практически во всех командах
процессора.
Типовые операции в разных процессорах похожи. Поэтому, изучив один раз язык
ассемблера одного процессора, Вы сможете освоить и ассемблер любого другого
процессора. Мнемокоды команд ARM-процессоров достаточно просты, например, «B» от
англ. branch – ветвление, переход, передача управления. Однако, они могут содержать и
целый ряд дополнительных суффиксов и префиксов, которые уточняют или меняют
содержание команды, например, «BNE», - перейти, если «не эквивалентно». Префикс «V»
будут иметь, в частности, все команды сопроцессора поддержки операций с плавающей
точкой. Он будет как-бы их визитной карточкой. При разработке мнемоники системы
команд создатели процессоров пользуются четкой логикой, поняв которую, Вы сможете
быстро освоить ассемблер. Мы поможем Вам в этом, последовательно объясняя логику
разработчиков. Более того, мы будем изучать программирование на ассемблере не с точки
зрения «Какие команды есть в системе команд?», а с точки зрения «Зачем они нужны и
как их использовать для решения наиболее часто встречающихся практических задач?».
Итак, Ассемблер – язык мнемокодов конкретного процессора. Сколько различных
процессоров – столько же и языков Ассемблер. Более того, для одного и того же
процессора может существовать несколько версий языка Ассемблер. Говорят, что
Ассемблер – машинно-зависимый язык. Но, это уже язык мнемокодов, который понятен
программисту, на нем можно писать и редактировать программы. Для перевода
программы, написанной на языке Ассемблер, в машинный код, разработчики процессоров
создают специальные программы – трансляторы с языка Ассемблер. Это программы30

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
переводчики, которые получают на входе файл, написанный на языке Ассемблер
конкретного процессора (созданный в одном из текстовых редакторов на компьютере), и
создают в результате трансляции выходной файл в машинном коде, который может быть
загружен в кодовую память и выполнен. Процесс трансляции программы с конкретного
языка Ассемблер в машинный код называется ассемблированием, сама программа на
компьютере, которая выполняет этот перевод – ассемблером.
Ассемблер – это язык программирования, который позволяет наиболее полно
использовать все имеющиеся возможности конкретного процессора. Но, это достаточно
сложный язык, требующий времени и значительных усилий для его освоения. В отличие
от Ассемблера, языки высокого уровня, такие как С/С++, являются машиннонезависимыми языками. Преимущество их использования состоит в том, что программа,
написанная на языке высокого уровня, может быть выполнена на любом процессоре.
Правда, для этого необходима уже другая программа – компилятор с языка высокого
уровня в язык используемого процессора. Обычно компилятор сначала «переводит»
программу с языка высокого уровня в программу на языке Ассемблер определенного
процессора, а затем транслирует ее в машинный код с помощью транслятора с языка
Ассемблер. Хотя сам язык высокого уровня машинно-независим, программыкомпиляторы являются машинно-зависимыми.
На современном уровне развития микропроцессорной техники для разработки,
отладки и загрузки программы в память микропроцессорной системы используются так
называемые интегрированные среды разработки – IDE. Они включают в себя не только
трансляторы и компиляторы для целевого процессора (выбранного разработчиком), но и
целый ряд дополнительных программ, обеспечивающих программиста удобными
средствами для написания и отладки программ: специализированные тестовые редакторы,
отладчики, загрузчики и т.д. Эта книга написана с использованием одного из таких
пакетов программ Keil μVision IDE, основные возможности которого для написания и
отладки программ будут постепенно раскрываться.
Ассемблер – язык программирования низкого уровня, который
позволяет максимально эффективно использовать все возможности, как
архитектуры процессора, так и его системы команд. Для получения
программы в исполняемом машинном коде используется транслятор с
Ассемблера.

2.5 Системы счисления, используемые в процессорной
технике
В быту и в физике мы пользуемся позиционной десятичной системой счисления –
DEC (Decimal), в которой каждое число представляется десятичными цифрами (0,1,2…9),
вес которых определяется позицией соответствующей десятичной цифры в числе табл. 2.2.
Таблица 2.2 Веса десятичных разрядов числа
Позиция
Вес



i
10

i



2
10

1
2

10

0
1

10

-1
0

10

-2
-1

10

-2



-q



10-q

Положение
десятичной точки

31

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
Здесь i – номер десятичной цифры в целой части числа, q – в дробной части.
Например, целое число 137 можно представить как 1×102+3×101+7×100, дробь 0.345 как
3×10-1+4×10-2+5×10-3, а вещественное число 77.23 как 7×101+7×100+2×10-1+3×10-2.
В микропроцессорной технике минимальной единицей информации, которая может
храниться в памяти, является бит. Однобитовая память статического типа (SRAM)
представляет собой обычный D-триггер (триггер данных). Совокупность некоторого числа
D-триггеров образует ячейку памяти заданной разрядности или регистр. Каждый разряд
ячейки памяти или регистра является битом и может принимать только два возможных
значения 0 или 1. Поэтому, в процессорной технике для представления любых данных
используется исключительно двоичная система счисления – Bin (Binary). Двоичная
система тоже является позиционной и в ней вес любого разряда числа определяется
степенью основания системы счисления 2n, где n – номер разряда, отсчитываемый от
положения двоичной точки – табл. 2.3. Само положение двоичной точки никак не
фиксируется аппаратными средствами (под нее не отводится ни один разряд числа) – это
прерогатива исключительно программиста. Только он знает, между какими разрядами
числа стоит эта «виртуальная» двоичная точка в данных, с которыми он работает.
Таблица 2.3 Веса двоичных разрядов числа
Позиция
Вес
Dec



i
2

i



2
2
4

1
2

2
2

0
1

2
1

-1
0

-1

2
0.5

-2
-2

2
0.25



-q



2-q

Положение двоичной
точки
Как видите, в двоичных числах можно использовать только две цифры: 0 и 1, вес
которых зависит от положения двоичной цифры внутри числа. Десятичные цифры в
программах на Ассемблере обычно записываются с суффиксом «d» в конце числа или без
какого-либо суффикса, двоичные – с суффиксом «b». Например, целое число 15 может
быть представлено как 1111b = 1×23+1×22+1×21+1×20 =1×8+1×4+1×2+1×1. Вещественное
число 1.75d, в котором под целую часть отведено два разряда и два разряда под дробную,
так: 01.11b. Для того, чтобы конвертировать двоичное число в десятичное, нужно сложить
десятичные веса всех единичных разрядов этого числа.
Иногда система счисления, в которой записано число, указывается основанием
системы счисления в виде подстрочного индекса в конце числа, например, 388 10 , 110012.
В языке Ассемблер ей соответствует следующий вариант записи: сначала указывается
основание системы счисления, а после него, через символ нижнего подчеркивания –
последовательность цифр в данной системе счисления, например, 10_4888, 2_1101001.
Конечно, многоразрядные двоичные числа не очень удобны для восприятия
человеком. На помощь приходит восьмеричная и шестнадцатеричная системы
счисления. В восьмеричной системе можно использовать только 8 цифр (0,1, 2, …7), а в
шестнадцатеричной – 16 (0,1, 2, …,8,9,A,B,C,D,E,F). Эти системы удобны тем, что каждые
три бита двоичного числа можно заменить одной восьмеричной цифрой, а каждые четыре
бита двоичного числа – одной шестнадцатеричной. Запись становится более компактной,
например: 254d = 11111110b = 0FEh = 15×161+14×160 = 240+14, или в восьмеричной
системе счисления 254d = 376q = 3×82+7×81 +6×80 = 192+56+6.
Шестнадцатеричное число записывается с суффиксом «h» в конце числа, а
восьмеричное – с суффиксом «q». Для записи 32-разрядного адреса в шестнадцатеричной
системе счисления потребуется всего 8 цифр. Так, адресный диапазон 32-разрядного
процессора ARM можно записать в виде: 00000000h÷0FFFFFFFFh. Заметьте, что, если
первой шестнадцатеричной цифрой является буква, то перед ней в Ассемблере нужно
обязательно указывать 0.
32

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
В Ассемблере и в языке С/С++ используется запись шестнадцатеричных чисел в
виде: 0x(последовательность шестнадцатеричных цифр). Максимальный 32-разрядный
адрес выглядит при этом так: 0xFFFFFFFF.
Пример конвертирования 32-разрядного двоичного числа в шестнадцатеричное
путем замены каждых четырех бит одной шестнадцатеричной цифрой показан ниже:
1 0
A

1

0 1
8

0 0

0

0 1
7

1 1

1 1 1
F

1 0
3

0

1 1

1 0
B

1 1

1 1 1
F

1 0
6

1 1

0

Полученное число записывается в виде 0A87F3BF6h или 0xA87F3BF6.
Заметим, что в шестнадцатеричном формате в процессорной технике могут быть
представлены и числа с плавающей точкой, а также любая другая информация – символы,
строки, то есть любая произвольная последовательность бит.
Данные в микропроцессорных системах представляются только в
двоичном коде – последовательностью бит. Для их более компактного
описания применяются восьмеричная и шестнадцатеричная системы
счисления. В языке Ассемблер допускается запись констант в любой системе
счисления, удобной для программиста. Функцию автоматического перевода
таких констант в двоичный код берет на себя транслятор с Ассемблера.

2.6 Сопроцессоры
Для расширения диапазона чисел, которые могут обрабатываться в процессоре,
разработан специальный формат их представления – формат чисел с плавающей
точкой. Обработка таких чисел возможна как программным путем с использованием
специальных библиотек, так и аппаратным – с применением дополнительных модулей,
интегрированных либо на процессорную плату, либо непосредственно на кристалл
базового процессора, что на порядок быстрее. Эти модули получили название
сопроцессоров. Хорошо известны сопроцессоры поддержки вычислений с плавающей
точкой фирм Intel 80387 и Motorola 68881, которые в свое время позволили существенно
поднять скорость вычислений в персональных компьютерах.
Наиболее часто используются сопроцессоры поддержки вычислений с
плавающей точкой (Floating Point Unit – FPU), хотя разработаны и другие типы
сопроцессоров (графические, для векторных вычислений и т.д.). Сопроцессоры имеют
свою собственную систему команд и определенный интерфейс с центральным
процессором.
Процессорные ядра Cortex-M4F содержат интегрированный на кристалл ЦПУ
сопроцессор поддержки вычислений в формате с плавающей точкой однократной
точности (32 разряда). Этим они заметно отличаются от большинства существующих
серий 8- и 16-разрядных микроконтроллеров, которые не имеют встроенного
сопроцессора. На рынок вышли микроконтроллеры, вычислительные возможности
которых уже сопоставимы с возможностями процессоров для современных компьютеров.
В них обеспечивается поддержка цифровой обработки сигналов (DSP) как с числами в
формате с фиксированной точкой, так и с плавающей точкой, которая ранее была
доступна исключительно для так называемых сигнальных процессоров и сигнальных
микроконтроллеров.

33

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
Вычислительные возможности рассматриваемых в этой книге
процессоров Cortex-M фирмы ARM не уступают, а порой и превосходят
возможности так называемых сигнальных процессоров (DSP), которые до сих
пор использовались для решения самых сложных задач управления
объектами в реальном времени.

2.7 Прерывания и исключения
2.7.1 Прерывания
Процессор может работать, общаясь с внешним миром путем опроса регистров
ввода данных с внешних датчиков, их обработки и вывода результатов обработки в виде
управляющих сигналов через порты вывода на исполнительные устройства. Однако,
такой режим работы «по опросу» крайне неэффективен. Программа должна постоянно
опрашивать большое число датчиков, чтобы не упустить важную информацию, что
требует значительных ресурсов процессора (памяти и времени).
Уже первые попытки использования компьютеров для целей управления
промышленными объектами (в 50-х годах прошлого века) показали, что программный
опрос датчиков, так называемый, «программный поллинг» – крайне неэффективен.
Представьте себе, что в системе имеется ряд аварийных датчиков: короткого замыкания,
пожара, превышения допустимого уровня какого-либо вещества, его концентрации и т.д.
Они срабатывают крайне редко и даже могут никогда не сработать (при соблюдении
правил эксплуатации системы). Тем не менее, состояние этих датчиков должно быть
известно цифровой системе управления в любой момент времени, чтобы быстро принять
соответствующие меры, например, отключить питание, включить сирену и т.п.
Поэтому, в состав любой микропроцессорной системы обязательно включается
контроллер прерываний – устройство, которое, работая в автономном режиме, постоянно
отслеживает некоторые события и при их возникновении определенным образом
информирует о них процессор. События могут быть как внешними, возникающими во
внешнем по отношению к микроконтроллеру оборудовании (например, перегрев
двигателя или насоса в результате перегрузки, перенапряжение, короткое замыкание), так
и внутренними, возникающими во встроенных в процессор или микроконтроллер
периферийных устройствах. В любом случае устройство, детектирующее такие ситуации,
должно сообщить об этом событии контроллеру прерываний, выставив сигнал запроса
прерывания (ЗПР). Различают запросы прерывания, поступающие по уровню сигнала
(статические входы) или по фронту (динамические). Чаще всего в процессорной
технике используются запросы прерываний по переднему фронту импульса.
Примером внутреннего события может быть завершение преобразования
аналогового сигнала с внешнего датчика (например, давления, расхода, температуры и
т.д.) в цифровой код. При этом АЦП выставляет сигнал готовности данных в контроллер
прерываний. Что должен сделать контроллер прерываний? Информировать процессор, что
имеется запрос на немедленную приостановку обычного хода выполнения программы –прерывание и выполнение специальных действий по обслуживанию запроса. В случае с
АЦП это достаточно простые действия: обратиться в выходной порт АЦП и считать из
него преобразованные в цифровой код данные, сохранить их в соответствующей
переменной в ОЗУ для последующей обработки. После выполнения этих действий
управление можно вернуть основной программе, в которой рассчитываются управляющие
воздействия (выполняется основной алгоритм управления).
Программа, выполняющая действия по обслуживанию запроса прерывания,
называется подпрограммой обработки прерывания или обработчиком прерывания.
Начальные адреса всех обработчиков прерываний размещаются в так называемой
34

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
таблице векторов прерываний, расположение которой в памяти каждого конкретного
процессора фиксировано. Для каждого запроса прерывания выделяется в памяти место, в
котором должен размещаться начальный адрес его обработчика – вектор прерывания.

ЗПР_0

Встроенные в МК
Встроенные в МП

ЗПР_2

ЗПР_N

РПР

Процессор

Периферийные
устройства

ЗПР_1

Контроллер
прерываний

Внешние датчики

Память
n=3

Обработчик
прерывания_3
Таблица векторов
прерываний
АДР_ОБР_N

ЗПР
АДР_ОБР_3
АДР_ОБР_2
АДР_ОБР_1
АДР_ОБР_0

Рис. 2.2 Технология обслуживания прерываний в МПС
Тогда работу контроллера прерываний упрощенно можно представить так (рис.
2.2):
1. Возникает сигнал запроса прерывания от какого-то устройства.
2. Контроллер прерывания информирует процессор о возникшем запросе (ЗПР) и,
получив от него разрешение на обслуживания прерывания (РПР), сообщает
процессору номер возникшего запроса прерывания.
3. Работа основной программы приостанавливается. Содержимое важнейших регистров
процессора (контекст) автоматически сохраняется в специальной области памяти (в
стеке) вместе с адресом продолжения программы (адресом возврата).
4. По номеру поступившего запроса прерывания, процессор обращается к таблице
векторов прерываний и считывает из нее адрес начала обработчика соответствующего
прерывания.
5. Этот адрес загружается в счетчик команд PC и управление передается подпрограмме
обслуживания прерывания.
6. В конце этой подпрограммы – обработчика прерывания – восстанавливается контекст,
и управление возвращается в основную программу.
Что главное? То, что в памяти процессора должна находиться таблица с
начальными адресами всех возможных обработчиков прерываний. По номеру запроса
прерывания процессор должен автоматически обратиться к определенной позиции в этой
таблице и считать из нее адрес соответствующего обработчика прерывания. Таблица
должна быть запрограммирована пользователем, как и подпрограммы всех обработчиков
прерываний.
Запросы прерываний могут поступать в контроллер прерываний от трех
источников: 1) от внешних датчиков (например, короткого замыкания, перегрева и т.п.); 2)
от периферийных устройств, интегрированных на кристалл микроконтроллера его
производителем (таймеров, генераторов периодических сигналов и т.д.); 3) от
периферийных устройств, встроенных в ЦПУ (например, от встроенных в процессоры
ARM системных таймеров).

35

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
Схема, показанная на рис. 2.2, – упрощенная. Контроллер прерываний – сложное
устройство, которое позволяет разрешить или заблокировать любой из запросов
прерываний, назначить запросу определенный уровень приоритета и т.д. В процессорах
ARM он называется контроллером вложенных векторных приоритетных прерываний
– Nested Vectored Interrupt Controller (NVIC). Термин вложенные отражает тот факт, что
более важные прерывания (имеющие более высокий приоритет) могут прерывать менее
важные (с меньшим приоритетом).

2.7.2 Исключения
Термин «исключение» очень похож на термин «прерывание». В процессе
выполнения программы пользователя могут произойти нештатные ситуации, такие как:
• Обращение процессора за кодом операции к области памяти, в которой коды операций
не могут находиться (в частности, к области памяти системной периферии
процессора);
• Считывание из программной памяти кода несуществующей операции;
• Выполнение операции деления на ноль и другие.
Нештатные ситуации в процессе выполнения программы называются
исключениями. Для каждой из них в процессорах ARM предусматриваются свои
собственные программы-обработчики. Их начальные адреса также, как и начальные
адреса подпрограмм обработки прерываний, заносятся в таблицу векторов прерываний
и исключений. В аппаратную часть процессора включается специальный модуль –
детектор исключительных ситуаций.
Как только одна из таких ситуаций идентифицируется, происходит автоматическое
обращение процессора к таблице векторов прерываний и исключений за получением
начального адреса обработчика соответствующего исключения. Дальше аналогично
обслуживанию прерываний: сохранение контекста и адреса возврата в основную
программу (в стеке) и загрузка в счетчик команд PC начального адреса обработчика
исключения с передачей ему управления.
В процедуре обработки исключения программист должен решить, что же делать в
данной нештатной ситуации? Например, при делении на 0: можно вернуть в качестве
результата максимально возможное значение и продолжить выполнение программы; или
сообщить об ошибке оператору и прервать выполнение программы.
Заметим, что в большинстве 8- и 16-разрядных микроконтроллеров механизм
обработки исключений вообще отсутствует. В микроконтроллерах с ядром Cortex-M он
присутствует, что существенно повышает надежность работы программы и всей
цифровой системы управления. Это особенно важно в ответственных объектах, где сбой
может привести к серьезной аварии.
В системах управления оборудованием, работающих в реальном
времени с большим числом встроенных периферийных устройств, не
обойтись без контроллера прерываний. Надежность программного
обеспечения существенно повышается, если в процессор встроена система
детектирования нештатных ситуаций – исключений. Каждое исключение
обрабатывается подобно запросу прерывания.
1) Какие три обязательных устройства должны входить в состав
любой микропроцессорной системы?
2) В какой области памяти должна располагаться программа?
3) В каком регистре процессора находится код операции в процессе
его декодирования?
36

ГЛАВА 2. ВВЕДЕНИЕ В МИКРОПРОЦЕССОРНУЮ ТЕХНИКУ
4) Какой из регистров процессора отвечает за последовательность выборки команд из
кодовой памяти?
5) В чем преимущество «отображения регистров периферийных устройств на
память»?
6) В чем преимущество МПС с контроллером прерываний?
7) За счет чего повышается надежность программного обеспечения в МПС с
обработчиками исключений?

4)

5)
6)

7)

1) Процессор, память, устройства ввода/вывода;
2) В области кодовой (программной) памяти – обычно в ПЗУ или ППЗУ
(перепрограммируемом постоянном запоминающем устройстве), например,
во флэш-памяти (допускает электрическое стирание и запись новых данных);
3) В регистре команд;
Счетчик команд PC всегда содержит адрес очередной команды, подлежащей
выполнению. Автоматически инкрементируется на число байт, занимаемых в
памяти текущей командой.
В том, что для доступа к регистрам можно использовать любые команды
процессора, работающие с памятью.
Не нужно постоянно опрашивать состояние датчиков. Если событие произойдет,
оно «уведомит» об этом контроллер прерываний, который, в свою очередь,
сообщит процессору о том, что имеется запрос прерывания, и «уточнит» – от
какого именно устройства или датчика.
Исключаются непредсказуемые ситуации в работе программы. Например, при
считывании кода операции из области памяти, в которой код не может быть
размещен или которой вообще не существует в системе. Идентифицируются
некоторые аппаратные сбои, например, аварии шин МПС.

Список рекомендуемой литературы
1) Козаченко В.Ф. Микроконтроллеры: руководство по применению 16-разрядных
микроконтроллеров Intel MCS-196/296 во встроенных системах управления. – М.:
ЭКОМ, 1997. – 688 с.
2) Встраиваемые высокопроизводительные цифровые системы управления.
Практический курс разработки и отладки программного обеспечения сигнальных
микроконтроллеров TMS320x28xxx в интегрированной среде Code Composer
Studio: учеб. пособие / А.С. Анучин, Д.И. Алямкин, А.В. Дроздов и др.; под общ.
ред. В.Ф. Козаченко, – М.: Издательский дом МЭИ, 2010. – 270 с.
3) Анучин А.С., Козаченко В.Ф. Архитектура, система команд, технология
проектирования и отладки специализированных сигнальных микроконтроллеров
для управления двигателями: Лабораторный практикум. – М.: Издательство МЭИ,
2001. – 96 с.

37

ГЛАВА 3. ОСНОВЫ ДВОИЧНОЙ АРИФМЕТИКИ

3 ОСНОВЫ ДВОИЧНОЙ
АРИФМЕТИКИ
Оглавление
3.1. Целые числа .......................................................................................................................... 39
3.2. Арифметика целых чисел без знака .................................................................................... 39
3.3. Диапазон возможных целых чисел без знака .................................................................... 41
3.4. Целые числа со знаком. Арифметика чисел со знаком..................................................... 41
3.5. Диапазоны целых чисел со знаком ..................................................................................... 44
3.6. Арифметика чисел большой разрядности .......................................................................... 45
3.7. Как в процессорах с большой разрядностью обрабатывать числа меньшей
разрядности? ................................................................................................................................ 46
3.8. Арифметика действительных чисел с фиксированной точкой ........................................ 47
3.8.1. Дробные числа без знака ............................................................................................. 47
3.8.2. Дробные числа со знаком в дополнительном коде ................................................... 48
3.8.3. Действительные числа с фиксированной точкой ...................................................... 48
3.8.3.1. Действительные числа с фиксированной точкой без знака ............................. 48
3.8.3.2. Действительные числа с фиксированной точкой со знаком ............................ 49
В этой главе мы рассмотрим типы данных, которые могут
обрабатываться в процессорах, а также основы двоичной арифметики. Эти
знания позволят Вам лучше понять внутреннее устройство процессорных
ядер Cortex-M, их архитектуру, а также оценить имеющийся в процессорах
набор команд с точки зрения их использования для обработки данных
разного типа.
Процессор работает с любой информацией как с простым набором бит. Никакого
специального признака типа данных и даже места расположения двоичной точки в числе
нет. Вся ответственность за содержимое данных и правильную их обработку, в том числе,
за интерпретацию результата вычислений лежит на программисте. Только он знает, что
представляют собой те или иные данные: целое число, вещественное, со знаком или без
знака, или даже – символ или строка символов. В соответствии с конкретным типом
данных, выбираются и нужные команды для их обработки.
Так же, как и в предыдущей главе, мы будем делать попутные замечания,
касающиеся основной темы книги – процессорных ядер ARM Сortex-M.
Большинство приводимых в этой главе примеров будет касаться «как бы 8разрядных процессоров» – в этом случае проще понять и проанализировать результат
операции. Однако, правила двоичной арифметики являются общими для процессоров
любой разрядности.

38

ГЛАВА 3. ОСНОВЫ ДВОИЧНОЙ АРИФМЕТИКИ

3.1 Целые числа
Целые числа могут быть любой разрядности 8-, 16-, 32-, 64-разрядными и более для
представления больших по величине чисел. Обычно разрядность числа кратна восьми,
числу бит в одном байте, хотя на практике возможны любые варианты, например, 10разрядные целые. Часто это определяется разрядностью периферийных устройств, с
которыми Вы работаете (например, информация, полученная с АЦП, может быть 10- или
12-разрядной). Обычно она размещается в младших разрядах соответствующих регистров
периферийных устройств, являющихся 16-разрядными – в полусловах с точки зрения 32разрядных процессоров ARM.
Целые числа могут быть как целыми числами без знака – Unsigned, так и целыми
со знаком – Signed. Будем обозначать формат целого числа количеством разрядов и
предшествующим ему префиксом «U» (для чисел без знака) или «S» (для чисел со
знаком). Таким образом, возможны обозначения: U8, S8; U16, S16; U32, S32 и т.д.
Применительно к процессорам ARM данные первого типа называются байтами,
второго – полусловами, третьего – словами. В 16-разрядных процессорах и
микроконтроллерах словами называются данные типов U16, S16. Это естественно, так как
именно эти числа обрабатываются в АЛУ 16-разрядных процессоров. В АЛУ процессоров
ARM обрабатываются 32-разрядные числа. Именно они называются словами.
Возможности процессоров ARM расширены так, что они могут обрабатывать не только
слова, но и полуслова и даже байты, причем – параллельно: по два полуслова или по
четыре байта одновременно.

3.2 Арифметика целых чисел без знака
Пример целого числа без знака 250 в формате U8 представлен ниже. Все восемь
двоичных разрядов числа вносят свой вклад в значение числа в соответствии со степенью
основания системы счисления 2n, где n - номер разряда. Знаковый разряд отсутствует.
Сложение двоичных чисел выполняется по
обычным математически правилам, «в столбик»,
начиная с младшего разряда. Если результат
сложения двух бит оказывается равным основанию
системы счисления (2), то он заменяется нулем и
формируется флаг переноса в следующий (старший)
Битовые операнды Результат
разряд С. Сложение всех последующих бит, кроме
X
Y
С
X+Y+C C*
младшего, выполняется «по модулю 2» с учетом
0
0
0
0
0
переноса из предыдущего разряда. Это означает, что
0
1
0
1
0
из результата, большего или равного 2, вычитается
1
0
0
1
0
двойка и формируется перенос в следующий разряд
1
1
0
0
1
С. В приведенной здесь таблице истинности
0
0
1
1
0
показаны исходные значения битовых операндов X и
0
1
1
0
1
1
0
1
0
1
Y и флага переноса С из предыдущего разряда,
1
1
1
1
1
результат сложения X+Y+C, сохраняемый в
соответствующем разряде суммы, и состояние флага переноса C* в следующий разряд
суммы.
Пример операции сложения чисел 250 и 3 показан ниже. Состояние флага переноса
С в следующий разряд отмечается левым надстрочным индексом рядом с битом суммы.
Результат сложения числа 253 не выходит за максимально возможное значение целого 8разрядного числа 255, следовательно, является достоверным:
Целое число без знака 250
D7 D6 D5
D4 D3
27
26 25
24 23
128 64 32
16 8
1
1
1
1
1

D2
22
4
0

D1
21
2
1

D0
20
1
0

39

ГЛАВА 3. ОСНОВЫ ДВОИЧНОЙ АРИФМЕТИКИ
D7
1
0
0
1

Первый операнд: 250d=0FAh
Второй операнд: 3d=03h
Результат сложения: 253d=0FDh

D6
1
0
0
1

D5
1
0
0
1

D4
1
0
0
1

D3
1
0
0
1

D2
0
0
0
1

D1
1
1
1
0

D0
0
1
0
1

Если при сложении двух самых старших бит числа возникает перенос, то это
свидетельствует о переполнении – выходе результата за пределы разрядной сетки
процессора (при работе с числами без знака).
Результаты всех арифметических и логических операций в АЛУ автоматически
анализируются процессором и могут отображаться специальными флагами в регистре
статуса программы пользователя. В процессорах ARM это происходит только тогда,
когда программист указывает в команде специальную опцию «S» (Set – установить флаги
результата операции). Один из таких флагов – флаг C (Carry – перенос) как раз и
выставляется при сложении чисел без знака в результате возникшего переноса из
последнего 31-го разряда.
Пусть выполняется арифметическая операция 254 + 3. Ее результат должен быть
равен 257 (256 + 1). Он превышает максимально возможное целое 8-разрядное число.
Поэтому, в 8-разрядном процессоре будет сформирован флаг переноса С. Единичное
значение этого флага как раз и соответствует числу 28=256. Процессор сообщает: в рамках
«моей» разрядной сетки получен результат 1, но не забудьте добавить к нему 256, если
считаете, что складывали два числа без знака (цветами выделены ниблы операндов):
С
Первый операнд: 254d=0FEh
Второй операнд: 3d=03h
Результат сложения:1d=01h

1

D7
1
0
1
0

D6
1
0
1
0

D5
1
0
1
0

D4
1
0
1
0

D3
1
0
1
0

D2
1
0
1
0

D1
1
1
1
0

D0
0
1
0
1

В 16- или 32-разрядом АЛУ флаг С будет выставляться только при выходе
результата за пределы 16- или 32-разрядной сетки, соответственно. Следовательно,
выполняя этот пример сложения на 16-разрядных или 32-разрядных процессорах, мы
получим правильный результат, не требующий коррекции: 257d=0000000100000001b.
Вычитание целых чисел без знака выполняется аналогично
сложению, начиная с младшего разряда. Отличие в том, что при
вычитании может вырабатываться флаг «заема» из более старшего
0 0 0 0
0
разряда числа в младший B (Borrow). Если «заем» возникает, то
0 1 0 1
1
для следующего разряда из уменьшаемого нужно вычесть не
1 0 0 1
0
1 1 0 0
0
только вычитаемое, но и флаг «заема». В таблице истинности
0 0 1 1
1
битового вычитания показаны исходные значения операндов X, Y,
0 1 1 0
1
а также бита «заема» B, сформированного при вычитании
1 0 1 0
0
младших бит. В качестве результата указано значение X-Y-B,
1 1 1 1
1
попадающее в соответствующий бит разности, а также значение
флага «заема» B* из следующего (старшего) разряда.
Обратите внимание на вторую строку в таблице истинности: чтобы из 0 вычесть 1,
нужно из более старшего разряда «занять» единицу; это будет соответствовать
формированию флага заема B*=1; вес этой «занятой» единицы на самом деле вдвое
больше – 2 в соответствии с весовыми значениями разрядов в двоичной системе
счисления; вычитая (2-1), получим 1, что и отображено в качестве результата в таблице
истинности.
Пример вычитания двух целых чисел без знака. Рядом с битовым результатом
левым надстрочным индексом показано состояние флага B:
Операнды
X Y B

Результат
X-Y-B B*

40

ГЛАВА 3. ОСНОВЫ ДВОИЧНОЙ АРИФМЕТИКИ
D7
0
0
0
0

Первый операнд: 83d=53h
Второй операнд: 58d=3Ah
Результат вычитания: 25d=19h

D6
1
0
0
0

D5
0
1
1
0

D4
1
1
1
1

D3
0
1
1
1

D2
0
0
0
0

D1
1
1
0
0

D0
1
0
0
1

В большинстве современных процессоров отдельного флага «Заема» B нет. В
качестве флага «Заема» используется флаг переноса С. Так же и в процессорах ARM: если
при сложении возникает переполнение, то флаг С устанавливается. Если при вычитании
возникает заем, то флаг С сбрасывается, а если заем не возникает, то, напротив,
устанавливается. Можете для простоты считать, что при выполнении операции вычитания
целых чисел состояние флага С формируется как инверсное значение флага «Заема».

3.3 Диапазон возможных целых чисел без знака
Диапазон возможных значений чисел без знака определяется разрядностью числа
0÷(2 -1), где n – число разрядов числа. Для типовых форматов чисел без знака получим:
n

Таблица 3.1 Диапазоны значений целых чисел без знака
Формат

Диапазон возможных значений

U8
U16
U32

0÷255
0÷65,535
0÷4,294,967,295

Как видите, в 8-разрядных процессорах диапазон возможных значений очень
невелик, а в 32-разрядных вполне достаточен для большинства практических задач.

3.4 Целые числа со знаком. Арифметика чисел со знаком
Для представления в микропроцессорной системе целых чисел со знаком придется
для представления знака выделить какой-то один из имеющихся разрядов, например,
старший разряд числа. Теоретически для этого имеется несколько возможностей: 1)
указать в специальном разряде знак числа (sign-magnitude), оставив для представления
собственно значения числа все остальные разряды; 2) представить число в обратном коде
(в виде дополнения всех разрядов числа до единицы - one’s complement; 3) представить
число в дополнительном коде - в виде дополнения до двух (two’s complement).
Первый способ, на первый взгляд, кажется наиболее простым и называется
представлением числа в абсолютном коде. Так, числа +9 и -9 в 8-разрядном формате S8
будут выглядеть так:
Число +9 в абсолютном коде
S|7
6
5
4
3
2
0
0
0
0
1
0

1
0

Число -9 в абсолютном коде
S|7
6
5
4
3
1
0
0
0
1

0
1

2
0

1
0

0
1

Значение самого старшего разряда определит знак числа: 0 – положительное, 1–
отрицательное. То же отрицательное число -9 в 16-разрядном формате будет представлено
так:
S|15
1

14
0

13
0

12
0

11
0

10
0

9
0

8
0

7
0

6
0

5
0

4
0

3
1

2
0

1
0

0
1

Обычные двоичные операции сложения и вычитания чисел в абсолютном коде
дают неверный результат. Чтобы результат был правильным, необходимо либо
41

ГЛАВА 3. ОСНОВЫ ДВОИЧНОЙ АРИФМЕТИКИ
корректировать его, либо вводить в систему команд специальные операции типа:
сложение, вычитание чисел со знаком в абсолютном коде. Поэтому, в процессорной
технике этот способ представления знаковых чисел практически не используется.
Второй способ представления отрицательных чисел предполагает побитовое
инвертирование соответствующего положительного числа – получение так называемого
обратного кода положительного числа. Например, для числа -9, получим код 11110110b.
Простое двоичное сложение таких одинаковых по величине положительных и
отрицательных чисел даст единицы во всех разрядах – получим «отрицательный ноль».
Значит, будут два нуля +0 и -0, что не очень удобно. Более того, результат двоичного
сложения таких чисел опять дает результат, требующий коррекции или использования
специальных команд для работы со знаковыми числами. Оба метода не удобны и редко
применяются на практике.
В процессорной технике используется представление отрицательных чисел
исключительно в дополнительном коде – третий метод. Это такое число, которое, будучи
добавлено к своему положительному аналогу с использованием обычного двоичного
сложения, даст ноль в разрядной сетке. По идее так и должно быть (+a)+(-a)=0. Этот
метод называют также дополнением до двух. Что имеется в виду? То же самое, что и при
сложении двух бит – результат должен обнулиться и возникнуть перенос в следующий
разряд. Применительно к процессорам с разрядной сеткой любой длины: отрицательное
число это такое, которое будучи прибавлено к соответствующему положительному числу
с использованием обычной операции двоичного сложения, даст ноль в разрядной сетке и
сформирует флаг переноса С за пределы разрядной сетки.
Технология получения отрицательного числа в дополнительном коде проста:
1) Получить обратный код положительного числа такой же величины путем побитового
инвертирования всех разрядов, включая старший.
2) Добавить число с единицей в младшем разряде – 01b. Полученный результат и будет
отрицательным числом в дополнительном коде.
Пример получения дополнительного кода числа (-2):
D7
0
1
0
0
1

Положительное число +2:
Обратный код числа +2:
Прибавить единицу
Число (-2) в дополн. коде: 11111110b

D6
0
1
0
0
1

D5
0
1
0
0
1

D4
0
1
0
0
1

D3
0
1
0
0
1

D2
0
1
0
0
1

D1
1
0
0
0
1

D0
0
1
1
1
0

При получении обратного кода используется обычная операция двоичного
побитового сложения. Проверим, будет ли сумма (+2) + (-2) давать нулевой результат?
С
Первый операнд: (+2)
Второй операнд: (-2)
Результат операции сложения: 0

1

D7
0
1
1
0

D6
0
1
1
0

D5
0
1
1
0

D4
0
1
1
0

D3
0
1
1
0

D2
0
1
1
0

D1
1
1
1
0

D0
0
0
0
0

Действительно, при сложении чисел с противоположным знаком в дополнительном
коде, получаем внутри разрядной сетки ЦПУ ноль. Правда, при этом возникает флаг
переноса С. Возникает вопрос, что с ним делать? Ответ такой: ничего, этот флаг имеет
значение только в операциях с целыми числами без знака. Для контроля переполнений в
операциях знаковой арифметики разработчики процессоров предусмотрели другой флаг V
(Overflow) – переполнение. Именно он будет сигнализировать о том, что результат
операции вышел за пределы представления знаковых чисел в рамках разрядной сетки
процессора.
42

ГЛАВА 3. ОСНОВЫ ДВОИЧНОЙ АРИФМЕТИКИ
Главное преимущество дополнительного кода в том, что арифметические операции
сложения и вычитания чисел без знака и со знаком можно выполнять по единой
технологии, одними и теми же командами. Есть только одно отличие – для контроля
переполнений нужно анализировать не флаг С, а флаг V. Но, это уже дело не процессора,
а программиста – он должен знать, с числами в каком формате работает!
Графическая
иллюстрация
представления 8-разрядных чисел в виде
чисел без знака U8 и со знаком в
дополнительном коде S8 показана на рис.
3.1. В первом случае числа занимают весь
диапазон возможных кодов (0-255), а во
втором – диапазон делится ровно на две
части: положительные числа (от +0 до +127)
и отрицательные числа (от -1 до -128). Ноль
считается условно положительным числом.

Перенос С
255 0 1
2
254

-2 -1 +0 +1+2

3

Отрица- Положительные тельные

-

+

+3

129 128 127

+

-127
+127
-128

Переполнение V

Рис. 3.1 Целые числа без знака U8 и целые
числа со знаком в дополнительном коде S8

ПереносС
0 1
2

-2 -1 +0 +1+2

3

32769
32768
32767

+

-

+3

Отрица- Положительные тельные

+

Аналогично работают АЛУ в 16разрядных и 32-разрядных процессорах.
Отличие только в диапазонах представимых
в этих процессорах целых чисел. На рис. 3.2
показаны диапазоны целых чисел без знака
и со знаком в 16-разрядных процессорах.

+32767

65534
65535



-32767
-32768



В 8-разрядном процессоре:
При сложении чисел без знака и превышении (>=) суммой числа 256, из нее как бы
вычитается число 256 и формируется флаг переноса C (см. рис. 3.1). Говорят, что
сложение чисел без знака выполняется по модулю 256. Результат сложения любого
числа байт будет правильным с точностью до 256.
При сложении чисел со знаком, если результат больше максимально возможного
положительного числа (+127) или меньше минимально возможного отрицательного
числа (-128), формируется флаг переполнения V (см. рис. 3.1). Результат внутри
разрядной сетки не корректируется – это дело программиста.

Переполнение V

Рис. 3.2 Целые числа в 16-разрядных
процессорах

43

ГЛАВА 3. ОСНОВЫ ДВОИЧНОЙ АРИФМЕТИКИ

3.5 Диапазоны целых чисел со знаком
Диапазоны возможных значений целых чисел со знаком в дополнительном коде
определяются разрядностью числа от -2n-1 до +(2n-1-1), где n – число разрядов. Для
типовых форматов получим (табл. 3.2):
Таблица 3.2 Диапазоны значений целых чисел со знаком в дополнительном коде
Формат

Диапазон возможных значений

S8
S16
S32
S64

-128 ÷ +127
-32,768 ÷ +32,767
-2,147,483,648 ÷ +2,147,483,647
-263÷ +(263-1)

В 16-разрядных процессорах, не говоря уже о 32- и 64-разрядных процессорах,
диапазон представления целых чисел со знаком достаточен для решения множества
практических задач. Задача программиста существенно облегчается – необходимость
контроля переполнений возникает крайне редко.
Все арифметические операции над целыми числами без знака и
целыми числами со знаком в дополнительном коде выполняются одними и
теми же командами процессора. В первом случае для контроля
переполнения используется флаг переноса С, а во втором – флаг знакового
переполнения V.
1)
Может ли процессор отличить, c каким числом в 8-разрядном формате
он имеет дело: с числом без знака U8=255 или числом со знаком S8= -1?
2)
Представьте числа +1 и -1 в дополнительном коде в форматах S8, S16,
S32.
3) Может ли при сложении чисел в дополнительном коде с разными знаками возникнуть
переполнение? А при сложении чисел с одинаковыми знаками?
4) Мы говорили о возможности выполнения в 32-разрядном АЛУ процессоров ARM
одновременно двух арифметических операций над полусловами или четырех операций
над байтами. Как, по-вашему, будут ли в этом случае формироваться 2 или 4 флага C и
флага V?
1)
Нет. Никаких признаков текущего формата числа нет. Об этом знает
только программист.
2)
Числа +1 и -1 в форматах S8, S16, S32 – см. рис. 3.3.

44

ГЛАВА 3. ОСНОВЫ ДВОИЧНОЙ АРИФМЕТИКИ

7|S 6
0 0
15|S 14 13 12 11 10 9
0
0
0 0 0
0 0

5
0

4
0

3
0

2
0

1
0

0
1

4
0

3
0

2
0

1
0

0
1

8
0

7
0

6
0

5
0

31|S 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9
0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

8
0

7
0

6
0

7|S 6
1
1
15|S 14 13 12 11 10 9
1
1
1
1
1 1
1

8
1

7
1

31|S 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9
1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

8
1

4
0

3
0

2
0

1
0

0
1

5
1

4
1

3
1

2
1

1
1

0
1

5
1

4
1

3
1

2
1

1
1

0
1

6
1
7
1

5
0

6
1

5
1

4
1

3
1

2
1

1
1

0
1

Рис. 3.3 Представление чисел +1 и -1 в различных форматах
3) Нет. При сложении чисел с разными знаками результат всегда будет по модулю
меньше самого большого положительного или отрицательного числа. Переполнения
исключаются. Именно при сложении чисел с одинаковыми знаками возможно
«знаковое» переполнение, о чем и будет свидетельствовать возникший флаг V.
4) Нет. Эти флаги формируются исключительно при работе с числами, разрядность
которых соответствует разрядности АЛУ. При параллельной работе с байтами
выполняется сложение и вычитание по модулю 256, а при работе с полусловами – по
модулю 65536. Все переносы и «заемы» игнорируются. Тем не менее, процессоры
ARM предоставляют программистам дополнительные возможности анализа
переполнений при выполнении параллельных арифметических операциях (см.
приложение 1).
Для увеличения разрядности целого числа со знаком в
дополнительном коде достаточно просто расширить знаковый разряд числа
на все старшие разряды (см. рис. 3.3). Эта операция называется знаковым
расширением
числа.
Для
ее
выполнения
предусматриваются
соответствующие команды процессора, например, загрузки в регистр общего
назначения (32-разрядный) байта или полуслова с автоматическим распространением
знакового разряда в область старших разрядов.

3.6 Арифметика чисел большой разрядности
Разрядность арифметико-логического устройства (АЛУ) любого процессора
фиксирована. Значит ли это, что он не может обрабатывать числа большей разрядности?
Нет. В любом процессоре арифметические операции возможны над словами, имеющими
любое число разрядов, кратное числу разрядов АЛУ: nоперанда=k×nАЛУ. Пусть складываются
или вычитаются два операнда, каждый из которых состоит из нескольких слов от
«Младшего слова» до «Старшего слова», и АЛУ процессора может обрабатывать слова
данной разрядности. Тогда, сложение или вычитание «длинных» слов можно выполнить
последовательно, как бы «в столбик», начиная с самого младшего слова:
• Младшие слова складываются с помощью обычной операции сложения ADD. При
этом может сформироваться флаг переноса C. Все последующие (более старшие) слова
складываются с помощью специальной операции сложения с учетом ранее возникшего
переноса ADC.

45

ГЛАВА 3. ОСНОВЫ ДВОИЧНОЙ АРИФМЕТИКИ


Младшие слова вычитаются с помощью обычной операции вычитания SUB. При этом
может сформироваться флаг «заема» B. Все последующие (более старшие) слова
вычитаются с использованием специальной операции вычитания с учетом ранее
возникшего «заема» SBB.

C C
C
Ст. слово Оп1 …
+
Ст. слово Оп2 …

C
Слово Оп1

C
Слово Оп1

Мл. слово Оп1

Слово Оп2

Слово Оп2

Мл. слово Оп2

ADC или SBB ADC или SBB ADC или SBB ADC или SBB ADD или SUB
При такой технологии сложения/вычитания результат будет правильным как для
целых чисел без знака, так и для чисел со знаком в дополнительном коде. Более того,
результат будет правильным и для вещественных чисел с фиксированной точкой, при
условии, что положение двоичной точки в исходных операндах одно и то же. Наличие
флага C или флага V после операции сложения самых старших слов будет
свидетельствовать о переполнении при работе с числами без знака или со знаком,
соответственно.
Для работы с числами любой разрядности, превосходящей
разрядность АЛУ, процессор должен иметь два типа команд
сложения/вычитания: 1) без учета ранее возникшего переноса/заема; 2) с
учетом ранее возникшего переноса/заема.

3.7 Как в процессорах с большой разрядностью
обрабатывать числа меньшей разрядности?
Для этого необходимо предварительно преобразовать число в меньшем формате в
число в большем формате, например, для 32-разрядных процессоров ARM: S8→S32 или
S16→S32. Технология преобразования:
• Если число без знака (U8, U16), расширить его в область старших разрядов нулями.
• Если число со знаком в дополнительном коде (S8, S16), следует расширить его в
область старших разрядов знаковым разрядом (см. рис. 3.3).
После такого преобразования форматов можно пользоваться любыми
арифметическими командами, причем часто без опасности переполнения, если число
операндов ограничено.
1) Сколько чисел без знака U8 можно сложить в 16-разрядном АЛУ
без опасности возникновения переполнения? В 32-разрядном АЛУ?
2) Сколько чисел со знаком S16 (полуслов) можно сложить в 32разрядном процессоре без опасности возникновения переполнения?

46

ГЛАВА 3. ОСНОВЫ ДВОИЧНОЙ АРИФМЕТИКИ

1)
Разделим максимально возможное число без знака U16max=65535
на максимально возможное число без знака U8max=255, получим 257. Для
32-разрядного процессора: U32max/U8max=4,294,967,296/255=16,843,009.
2) Разделим максимально возможное положительное число в формате
S32 на максимально возможное положительное число в формате S16,
получим: S32max/S16max=2,147,483,647/32,767=65538.
Как видно из приведенных выше примеров, в процессорах с большой разрядностью
удобно обрабатывать массивы чисел с меньшей разрядностью. При этом за счет
расширенной разрядной сетки процессора вероятность возникновения переполнения
практически исключается.
Процессоры ARM, имея 32-разрядное АЛУ, могут эффективно
обрабатывать и числа меньшей разрядности: 8- и 16-разрядные. Перед этим
необходимо только выполнить операцию преобразования исходного формата
числа в 32-разрядный формат.
Если арифметические операции с байтовыми числами (U8 или S8) или
с полусловами (U16, S16) должны выполняться без контроля переполнений, то это можно
сделать специальными командами параллельного сложения/вычитания сразу всех байт
или всех полуслов исходных 32-разрядных операндов.

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

3.8.1 Дробные числа без знака
Это числа, в которых двоичная точка «стоит» (виртуально) перед самым старшим
разрядом, т.е. под целую часть числа не отведен ни один разряд. Весовые коэффициенты
двоичных разрядов в направлении от старшего разряда к младшему равны: 2-1, 2-2, 2-3, …,
или 0,5; 0,25; 0,125… Пример 8-разрядной дроби без знака 0.625 показан ниже.
Дробное число без знака 0,625
D7
D6
D5
D4
2-1
2-2
2-3
2-4
0,5
0,25
0,125
0,0625
1
0
1
0

D3
2-5
0,03125
0

D2
2-6
0,015625
0

D1
2-7
0,078125
0

D0
2-8
0,00390625
0

Для обозначения формата вещественных чисел будем по-прежнему использовать
символ «U» или «S» для чисел без знака или со знаком, после которого указывать число
бит, выделенное под целую часть числа I, включая знаковый разряд, и, через точку –
число бит дробной части Q: U(I.Q) или S(I.Q). В соответствии с этим 8-разрядная дробь
без знака будет иметь обозначение U(0.8).

47

ГЛАВА 3. ОСНОВЫ ДВОИЧНОЙ АРИФМЕТИКИ

3.8.2 Дробные числа со знаком в дополнительном коде
Как и для целых чисел, в дополнительном коде старший разряд числа отводится
под знак (0 – положительное число, 1 – отрицательное), а все остальные разряды – под
значение дроби. Естественно, что число разрядов дробной части при этом сокращается на
один разряд. Пример числа +0.625 в 8-разрядном формате S(1.7) показан ниже:
Положительное дробное число +0,625
D7
D6
D5
D4
D3
S
2-1
2-2
2-3
2-4
0,5
0,25
0,125
0,0625
0
1
0
1
0

D2
2-5
0,03125
0

D1
2-6
0,015625
0

D0
2-7
0,078125
0

Технология получения отрицательных дробных чисел в дополнительном коде не
отличается от уже описанной выше для целых чисел. Получаем обратный код
положительного аналога дроби и добавляем 1 в самый младший разряд. Выполняем
обычное двоичное сложение. Так, для числа (-0,625) имеем:
D7
0
1
0
1

Положительная дробь +0,625:
Обратный код числа +0,625:
Прибавить +1 в младшем разряде
Число (-0,625) в дополнительном коде:

D6
1
0
0
0

D5
0
1
0
1

D4
1
0
0
1

D3
0
1
0
1
0

D2
0
1
0
1
0

D1
0
1
0
1
0

D0
0
1
1
1
0

Проверим, будет ли сумма положительной и отрицательной дроби равна нулю?
С
Первый операнд: (+0,625)
Второй операнд: (-0,625)
Результат операции сложения:

1

D7
0
1
1
0

D6
1
0
1
0

D5
0
1
1
0

D4
1
1
1
0

D3
0
0
0

D2
0
0
0

D1
0
0
0

D0
0
0
0

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

3.8.3 Действительные числа с фиксированной точкой
В этом случае определенное число старших двоичных разрядов I отводится для
представления целой части числа (включая знаковый разряд), а оставшееся число
разрядов Q используется для представления дробной части числа. Такой формат
называется I.Q-форматом.
3.8.3.1 Действительные числа с фиксированной точкой без знака
В таких числах знаковый разряд отсутствует и все разряды перед «виртуальной»
точкой используются для представления целой части числа U(I.Q). Ниже в качестве
примера показано число 2,25 в формате U(3.5):

48

ГЛАВА 3. ОСНОВЫ ДВОИЧНОЙ АРИФМЕТИКИ
Вещественное число без знака 2,25
D7
D6
D5
D4
D3
22
21
20
2-1
2-2
4
2
1
0,5
0,25
0
1
0
0
1

D2
2-3
0,125
0

D1
2-4
0,0625
0

D0
2-5
0,03125
0

3.8.3.2 Действительные числа с фиксированной точкой со знаком
В формате S(I.Q) самый старший разряд отводится под знак. То же число 2,25 в
формате S(3.5) представляется так:
Вещественное число со знаком 2,25
D7
D6
D5
D4
D3
S
21
20
2-1
2-2
2
1
0,5
0,25
0
1
0
0
1

D2
2-3
0,125
0

D1
2-4
0,0625
0

Технология получения дополнительного кода
фиксированной точкой такая же, как для целых чисел.

D0
2-5
0,03125
0

вещественных

чисел

с

1) Чем отличается формат числа U(16.16) от формата S(16.16)?
2) Нужно сложить два вещественных числа со знаком в форматах
S(24.8) и S(16.16). Как выровнять положения двоичной точки в исходных
операндах?
3) Будет ли при такой операции потеряна точность?
1) Первое число – без знака, под целую часть которого отведено 16
разрядов. Второе – число со знаком в дополнительном коде, под целую
часть которого, включая знак, также отведено 16 разрядов.
2) Это не простой вопрос. Нужно сделать операцию арифметического
сдвига второго операнда на 8 разрядов вправо. При этом положения
двоичной точки станут одинаковыми, т.е. формат S(16.16) будет
преобразован к формату S(24.8). При сдвиге вправо исходным знаковым разрядом
будут заполнены все освободившиеся при сдвиге старшие разряды, а младшие 8
разрядов будут утеряны. В новом формате получим то же значение операнда, но с
меньшим числом разрядов дробной части (вместо 16 разрядов – 8 разрядов).
3) Да, точность суммы будет на уровне точности операнда с меньшим числом разрядов
дробной части.
Заканчивая эту главу, отметим, что процессоры Cortex-M4 имеют дополнительную
поддержку операций по эффективной обработке сигналов, преобразованных в формат с
фиксированной точкой (команды умножения с накоплением и насыщения), а процессоры
Cortex-M4F – поддержку вычислений над числами в формате с плавающей точкой с
помощью интегрированного на кристалл сопроцессора. Эти возможности подробно
рассмотрены в последних главах книги.
Арифметические операции сложения и вычитания над вещественными
числами с фиксированной точкой выполняются теми же командами, что и
для целых чисел. Обязательным условием получения правильного результата
является операция «выравнивания положения двоичной точки» в исходных
операндах, реализуемая с помощью команд сдвига.

49

ГЛАВА 4. ОСНОВНЫЕ ТИПЫ ПРОЦЕССОРНЫХ АРХИТЕКТУР

4 ОСНОВНЫЕ ТИПЫ
ПРОЦЕССОРНЫХ АРХИТЕКТУР
Оглавление
4.1. Архитектура фон-Неймана .................................................................................................. 50
4.2. Недостатки фон-Неймановской архитектуры ................................................................... 52
4.3. Гарвардская архитектура ..................................................................................................... 53
4.4. Конвейерная архитектура RISC-процессоров ................................................................... 56
4.5. Модифицированная Гарвардская архитектура .................................................................. 58

4.1 Архитектура фон-Неймана
В современной компьютерной индустрии применяется несколько
типовых архитектур построения процессоров. Познакомимся с ними. Это
позволит оценить все достоинства и преимущества архитектуры
процессоров ARM Cortex-M, изучаемых в этой книге (см. главу 5).
Базовые принципы построения процессоров были заложены в 1944
году коллективом ученых, разработавших первый в мире компьютер на
электронных лампах «ЭНИАК» (Университет в Пенсильвании, США). По имени одного
из них, фон-Неймана, и был назван принцип совместного хранения в памяти кодов
программ и данных, известный сегодня как «Архитектура фон-Неймана». Процессоры с
фон-Неймановской архитектурой отличаются следующими особенностями (рис. 4.1):

ПРОЦЕССОР (ЦПУ)

Шина данных
ПАМЯТЬ

Регистр
команд

Коды команд и
данные

Блок декодирования команд
Шина
управления
Операционный блок

Устройство
управления и
синхронизации

Аккумулятор
Шина адреса
Регистры общего
назначения

Счетчик
команд PC

УВВ
(Периферийные
устройства)

Рис. 4.1 Упрощенная фон-Неймановская архитектура
1) Программное управление. Все действия, которые должен выполнить процессор,
описаны в программе, расположенной в памяти. Программа представляет собой набор
управляющих слов (кодов команд), которые «понятны» данному процессору, то есть
могут быть декодированы и выполнены.

50

ГЛАВА 4. ОСНОВНЫЕ ТИПЫ ПРОЦЕССОРНЫХ АРХИТЕКТУР
2) Последовательное выполнение команд. Команды считываются из памяти,
расшифровываются и выполняются последовательно. За порядком выполнения команд
следит специальный регистр процессора – счетчик команд PC, содержимое которого
автоматически модифицируется процессором в зависимости от длины текущей
команды. Он всегда содержит адрес очередной команды, подлежащей выполнению.
Последовательное выполнение команд может нарушаться специальными командами
условной или безусловной передачи управления, суть которых сводится к загрузке в
счетчик команд PC нового адреса.
3) Память адресуется исключительно процессором. Каждая ячейка памяти имеет свой
персональный адрес, по которому процессор может обратиться к ней по чтению или
записи, выставляя адрес этой ячейки на шину адреса. В памяти хранятся слова
информации в двоичном коде, значения которых может интерпретировать только
процессор, в зависимости от типа выполняемой в данный момент операции доступа к
памяти (получение кода очередной команды или получение данных).
4) Направление передачи данных от памяти к процессору (чтение) или от процессора к
памяти (запись) определяет только процессор, выставляя на шину управления либо
сигнал чтения, либо записи данных.
5) Память является однородной. Никакого специального признака типа хранимой в ней
информации (данные или код операции) в памяти нет. Первый раз обращаясь к памяти,
процессор «по умолчанию» считает, что расположенные там данные – код операции.
Код операции автоматически попадает в регистр команд и подвергается расшифровке.
Все последующие обращения процессора к памяти зависят от типа текущей команды.
Если эта команда требует получения данных из памяти, то в процессе ее выполнения
следует дополнительный цикл обращения к памяти, но уже за данными. Адрес этой
ячейки памяти генерирует процессор и выставляет на шину адреса. Считанные из
памяти данные попадают не в регистр команд, а в один из внутренних регистров
процессора, обычно в один из регистров общего назначения.
6) Периферийные устройства могут адресоваться как память. Они могут быть
подключены к тем же шинам, что и память. Регистры периферийных устройств имеют
адреса, отображаемые на память, что позволяет работать с ними как с обычными
ячейками памяти.
Большинство первых процессоров и микроконтроллеров имеют фонНеймановскую архитектуру. Признаки этой архитектуры присутствуют и в большинстве
других, современных процессорных архитектур.
Различают аккумуляторную и регистр-регистровую архитектуру. В первом случае
результат любой операции в АЛУ сохраняется в специальном регистре- аккумуляторе.
Более того, один из операндов может находиться в аккумуляторе по умолчанию.
Следовательно, в формате команды будет адресоваться только второй операнд, что
сокращает длину команды и делает ее выборку более быстрой. В ряде процессоров
используется и двух-аккумуляторная архитектура. Это позволяет сохранить результат
операции сразу в двух регистрах, например, при выполнении операции умножения, когда
произведение имеет формат двух слов.
При увеличении объема сверхоперативной памяти процессора – числа регистров
общего назначения, большая часть переменных может храниться не в памяти, а в
регистрах (внутри процессора). Так как доступ к ним максимально быстрый, это
повышает производительность МПС, одновременно сокращая формат команды (ее
длину): число регистров в регистровом файле ЦПУ всегда ограничено; для их адресации
может использоваться внутренняя шина адреса малой разрядности; поле адреса операнда,
находящегося в одном из внутренних регистров ЦПУ, может быть коротким.
Рассмотрим простой пример. Если шина адреса памяти 16-разрядная и нужна
команда сложения двух операндов в памяти, то при прямой адресации операндов для
51

ГЛАВА 4. ОСНОВНЫЕ ТИПЫ ПРОЦЕССОРНЫХ АРХИТЕКТУР
указания их адресов в формате команды потребуется битовое поле длиной (16+16) бит.
Это означает, что команда будет длиннее 4-х байт (с учетом кода операции) и будет долго
извлекаться из памяти. Если те же операнды расположены в регистрах ЦПУ, общее число
которых не превышает 256, то для указания адресов регистров-источников в формате
команды потребуется битовое поле длиной только (8+8) бит. Формат команды становится
существенно более коротким. Именно поэтому разработчики процессоров стремятся по
минимуму использовать прямую адресацию операндов в памяти, заменяя ее регистровой
адресацией операндов во внутренней памяти ЦПУ.
Регистр-регистровую архитектуру имеют многие современные 16-разрядные
микроконтроллеры. Большая часть операндов извлекается из регистров общего
назначения, в них же сохраняется результат операции. Как мы увидим далее, процессоры
ARM также работают преимущественно с данными, расположенными в регистрах общего
назначения, обращаясь к переменным в памяти только при необходимости (в основном
при загрузке/сохранении данных).

4.2 Недостатки фон-Неймановской архитектуры
Наличие общих шин для обращения к памяти программ и памяти данных является
узким местом первой процессорной архитектуры.
Представьте себе, что Вы работаете с 8-разрядным процессором Intel 8085, и
выполняется команда загрузки в аккумулятор данных из ячейки памяти по адресу 8000h
(шина адреса у этого процессора 16-разрядная): LDA 8000h (Load A –Загрузить
аккумулятор). При 8-разрядной памяти формат этой команды следующий: первый байт –
код операции; второй – младший байт 16-разрядного адреса ячейки-источника данных;
третий – старший байт этого адреса). Процессор, считав в регистр команд код операции,
расшифровывает ее и «понимает», что нужно загрузить данные из какой-то области
памяти, адрес которой еще находится в памяти (в двух следующих ячейках) – это и есть
так называемая прямая адресация памяти. Что он должен сделать? Сначала считать из
памяти младший байт адреса, затем старший байт (в какие-то внутренние регистры
процессора, обычно «теневые», невидимые для программиста) и только затем выставить
полученный адрес на шину адреса и прочитать данные из нужной ячейки памяти (или из
ОЗУ или из ПЗУ). Получается, что для выполнения команды процессору потребуется
целых четыре цикла доступа к памяти. А если из памяти требуется извлечь два операнда –
это уже 8 циклов. Все это время следующая команда будет ждать своей очереди на
выполнение.
Говорят, что такая архитектура имеет низкую пропускную способность канала связи
между памятью и процессором (интерфейса «Процессор» – «Память»). Этот недостаток
становится особенно заметным при росте производительности процессора.
Производительность процессора растет быстрее, чем быстродействие памяти, и именно
память
становится
тормозом
в
повышении
производительности
всей
микропроцессорной системы.
Часто фон-Неймановская архитектура процессора ассоциируется с архитектурой
процессоров с полным набором команд – CISC-архитектурой (Complete Instruction Set
Copmuter). Ее отличительная особенность – развитая система команд, которая допускает
адресацию операндов непосредственно в памяти. Как следствие – длинные форматы
команд (при прямой адресации), большое время, затрачиваемое процессором на
считывание собственно кодов команд из памяти.
Наличие в процессорах с фон-Неймановской архитектурой общих шин для
обращения и к памяти программ, и к памяти данных делает одновременный, параллельный
доступ к этим областям памяти невозможным. Это означает, что считывать очередной

52

ГЛАВА 4. ОСНОВНЫЕ ТИПЫ ПРОЦЕССОРНЫХ АРХИТЕКТУР
код команды из памяти и одновременно получать операнд из памяти для уже считанной
команды, находящейся на этапе выполнения, невозможно.
Второй существенный недостаток связан с принципом однородности памяти. И
память программ, и память данных находятся в общем адресном пространстве. Программа
может располагаться в общем случае как в ПЗУ, так и в ОЗУ. При этом архитектура
процессора не предполагает никаких аппаратных средств защиты области кодовой памяти
с расположенной там программой от преднамеренного или непреднамеренного доступа по
записи.
Расположенная в ОЗУ программа может быть подвержена действию вирусов или
внешних атак. Защитой от них является только размещение программного кода в
постоянной памяти (ПЗУ), запись в которую на стадии эксплуатации системы
невозможна. Так и делается в микропроцессорных системах встроенного управления
оборудованием. При этом программа размещается обычно в электрически
программируемом постоянном запоминающем устройстве (ЭППЗУ).
К этому типу памяти относится и широко распространённая сегодня флэш-память,
которая позволяет многократно стирать данные в памяти и записывать туда новые (до 10
тысяч циклов обновления без потери надежности). Ее отличительной особенностью
является то, что для программирования не требуются дополнительные устройства –
программаторы. Такой программатор встраивается в аппаратную часть самого
микроконтроллера, а с помощью специальных программ – загрузчиков флэш-памяти –
обеспечивает ее программирование, причем с обычного персонального компьютера по
одному из стандартных интерфейсов (например, JTAG). Такие программы встраиваются и
в интегрированные среды разработки, с которыми нам предстоит познакомиться.
В процессорах с фон-Неймановской архитектурой программа может
располагаться не только в ПЗУ, но и в ОЗУ, например, в процессе отладки.
Иногда некоторые функции могут специально размещаться в ОЗУ и
выполняться оттуда, в попытке повысить производительность системы. Это
возможно, так как быстродействие ОЗУ обычно выше быстродействия ПЗУ.
Однако, гарантия «целостности» программного обеспечения исчезает. Фрагмент
программы в ОЗУ не защищен от случайной записи, что может привести к сбою и даже к
аварии. Поэтому, в системах управления оборудованием рекомендуется располагать
программы преимущественно в ПЗУ (однократно программируемом при записи –
ОППЗУ) или во флэш-памяти, допускающей многократную «перепрошивку» –
перепрограммирование при модернизации программного обеспечения объекта. Только
при таком подходе случайная запись в кодовую память на этапе выполнения программы
становится аппаратно невозможной.

4.3 Гарвардская архитектура
Еще в 30-х гг. прошлого века (до второй мировой войны) в Гарвардском
университете США была разработана процессорная архитектура, преимущества которой
стали понятны лишь спустя несколько десятилетий. По иронии судьбы первый компьютер
был реализован по архитектуре конкурентов из Принстонского университета –
архитектуре фон-Неймана, описанной выше. Что же представляет собой Гарвардская
архитектура?
1) Физически разная память для хранения команд и данных (кодовая память и память
данных).
2) Физически разные интерфейсы «Процессор» – «Кодовая память» и «Процессор» –
«Память данных».

53

ГЛАВА 4. ОСНОВНЫЕ ТИПЫ ПРОЦЕССОРНЫХ АРХИТЕКТУР
3) Физически разные интерфейсы «Процессор» – «Кодовая память» и «Процессор» –
«УВВ».
Выполнение первого пункта можно обеспечить и в процессорах с фонНеймановской архитектурой. Два остальных пункта – отличительные. В этой архитектуре
имеются три полностью изолированных друг от друга интерфейса процессора с кодовой
памятью, памятью данных и устройствами ввода-вывода. При условии отображения
адресов регистров периферийных устройств на память данных, последние два интерфейса
становятся одним, общим.
Любая команда (например, сложение, умножение) требует получения двух
операндов, выполнения действий над ними в АЛУ и сохранения полученного результата в
памяти. Однако, код команды нужно сначала получить из памяти программ и
расшифровать. Если кодовая память физически отделена от памяти данных, то операции
получения/сохранения операндов и получения кода очередной команды можно
совместить, повысив производительность процессора. При этом интерфейсы процессора
с кодовой памятью и памятью данных могут существенно отличаться (по разрядности
слова данных, тактовой частоте, протоколу обмена данными) – рис. 4.2. Более того,
память данных может быть быстрой, а память программ – медленной, в том числе
рассчитанной только на чтение (с аппаратной блокировкой записи). Память данных может
быть малого объема (шина адреса малой разрядности), а память программ – большого
(шина адреса большой разрядности).
Невозможность записи в память программ на стадии выполнения программы
является средством повышения надежности микропроцессорной системы. Уменьшается
вероятность как непреднамеренного, так и умышленного («хаккерская» атака)
повреждения программы и программного сбоя.
В общем случае интерфейс процессора с периферийными устройствами может
также отличаться от интерфейса с памятью данных, как по разрядности слова на шине
данных, разрядности шины адреса, так и по протоколу обмена.
На рис. 4.2 не показана шина управления, которая, конечно, есть, но в этом случае
она будет своя для кодовой памяти, памяти данных и периферийных устройств. В
процессорах с Гарвардской архитектурой все три шины (шина данных, шина адреса и
шина управления) условно объединяются в одну и называются шинами интерфейса
процессора с кодовой памятью, памятью данных, периферийными устройствами. Для
каждой из таких шин разработчики процессоров определяют разрядность данных,
адресов, порядок обмена информацией, то есть протоколы обмена данными.
Итак, отличительная особенность Гарвардской архитектуры – возможность
параллельного выполнения нескольких действий сразу: считывания кода очередной
команды из кодовой памяти; чтения значений операндов из памяти данных или
сохранения в ней результата предыдущей операции. Параллельно могут выполняться
также операции получения очередной команды из кодовой памяти и чтения/записи в
устройства ввода/вывода. Если память данных является двухпортовой (допускающей
одновременную параллельную запись в нее данных по одному адресу и считывание
данных по другому адресу), то возможен еще больший параллелизм: считывание
очередного операнда для текущей операции и одновременное сохранение результата
предыдущей операции (так и делается в современных сигнальных процессорах).

54

ГЛАВА 4. ОСНОВНЫЕ ТИПЫ ПРОЦЕССОРНЫХ АРХИТЕКТУР

ПРОЦЕССОР (ЦПУ)

Интерфейс
кодовой
памяти
ПАМЯТЬ
ПРОГРАММ

Блок декодирования команд
Счетчик
команд PC

Операционный
блок
Устройство
управления и
синхронизации
Регистры общего
назначения

Интерфейс
памяти
данных

ПАМЯТЬ
ДАННЫХ

УВВ

Интерфейс ПУ

(Периферийные
устройства)

Рис. 4.2 Гарвардская архитектура процессоров
С одной стороны, использование этой архитектуры – существенное повышение
быстродействия системы, а с другой – значительное усложнение аппаратной реализации
процессора. Большое число шин означает также большое число выводов процессора, если
элементы памяти – внешние, а также наличие для каждой из них своего собственного
устройства управления и синхронизации. Именно усложнение аппаратуры задержало
разработку процессоров с Гарвардской архитектурой на десятилетия. Она стала
возможной только при резком повышении уровня интеграции транзисторов на кристалле
и удешевлении процессорных БИС.
Более высокий уровень надежности определил области использования этой
процессорной архитектуры – прежде всего встраиваемые в оборудование решения, где
отказ в работе может привести к серьезным последствиям. Практически все
разработанные в последние годы микроконтроллеры и сигнальные микропроцессоры
имеют Гарвардскую архитектуру, в том числе и рассматриваемые в этой книге ядра ARMCortex-M3/M4/M4F.
Процессоры с Гарвардской архитектурой имеют отдельные
оптимизированные интерфейсы с кодовой памятью, памятью данных и
периферийными устройствами, что позволяет совместить несколько
типовых операций по времени и резко поднять производительность
процессора. Гарвардская архитектура предполагает (опционально) наличие
дополнительных модулей управления памятью, которые имеют аппаратные средства
блокировки несанкционированного доступа к заданным программистом областям памяти
по записи для повышения надежности системы в процессе эксплуатации.

55

ГЛАВА 4. ОСНОВНЫЕ ТИПЫ ПРОЦЕССОРНЫХ АРХИТЕКТУР

4.4 Конвейерная архитектура RISC-процессоров
1-й такт
Загрузчик

Кодовая
память

1

2-й такт

Загрузчик

1

Кодовая
память

2

Дешифратор

3-й такт

1

Операци
-онный
блок

Загрузчик

2

Кодовая
память

3

Дешифратор

Практически
все
современные
процессоры,
имеющие
Гарвардскую
архитектуру, обрабатывают команды на так
называемом «конвейере команд» (рис. 4.3) и
являются
RISC-процессорами.
Задача
обработки
команды
разбивается
на
несколько этапов, на каждом из которых над
командой
выполняются
строго
определенные действия. Эти действия
зависят от места расположения команды на
конвейере. Каждое место (каскад конвейера)
имеет свой собственный обработчик
команды. В самом простом случае команду
необходимо, как минимум:
1) выбрать из памяти по интерфейсу
«Процессор»

«Кодовая
память».
Обработчик этого каскада извлекает
очередную команду из памяти и размещает
ее на конвейере – загружает конвейер
новой командой;
2) декодировать код операции уже
находящейся на конвейере команды –
определить, что же должен делать
операционный блок процессора, какая
микропрограмма обработки данных должна
быть запущена;
3) выполнить. Обработчиком этого
каскада конвейера является операционный
блок
процессора
(включая
АЛУ,
умножитель, делитель, сдвиговый регистр,
сопроцессор).

Рис. 4.3 Концепция конвейерной обработки
команд
Все этапы конвейера выполняются за одно и то же время, называемое тактом
конвейера (обычно равен одному такту процессора – периоду тактовых импульсов).
Выше мы привели список задач, характерный именно для 3-х уровневого конвейера
процессорных ядер ARM-Cortex-M3/M4/M4F.
На 1-м такте на пустой конвейер загружается команда № 1. Далее (на 2-м такте)
«конвейерная лента» продвигается на одну позицию влево и одновременно выполняются
два действия: загружается новая команда № 2; ранее загруженная команда № 1
дешифрируется. «Конвейерная лента» сдвигается на одну позицию и на 3-м этапе
одновременно выполняются уже три действия: декодированная команда № 1 выполняется
в операционном блоке процессора; команда № 2 декодируется, а команда № 3 загружается
на конвейер.

56

ГЛАВА 4. ОСНОВНЫЕ ТИПЫ ПРОЦЕССОРНЫХ АРХИТЕКТУР
Далее на каждом очередном такте одновременно будут выполняться сразу три
команды. Со стороны наблюдателя на выходе конвейера создается полное впечатление о
том, что каждая команда выполняется всего лишь за один такт.
На самом деле каждая команда находится на конвейере три такта, но на разных
этапах выполнения: загрузки, дешифрации, собственно выполнения.
Итак, конвейер команд очень похож на обычный автомобильный конвейер, на
каждом этапе которого над автомобилем выполняются строго определенные действия
(сварка кузова, окраска, установка двигателя, …, тестирование). На каждом этапе
конвейера установлено особое оборудование (например, сварочные роботы) и работают
специально обученные рабочие и инженеры. Команда в процессорной технике аналогична
автомобилю, который последовательно продвигается по конвейеру и подвергается
очередным этапам обработки – рис. 4.3.
Вот мы и подошли к понятию RISC-архитектура – Reduced Instruction Set
Computer – компьютер (процессор) с сокращенным набором команд. Главной
отличительной особенностью таких процессоров является выполнение большинства
команд за один такт процессора. Это возможно только в конвейерных архитектурах,
более того, в многошинных Гарвардских, когда отдельные интерфейсы с кодовой памятью
и памятью данных позволяют центральному процессору независимо и одновременно
обращаться к кодовой памяти для считывания кодов операций и к памяти данных для
получения или сохранения результатов выполнения команд.
Сокращенное число команд означает, что число команд в наборе конкретного
процессора не так велико и все они имеют одинаковый (или близкий) формат. Так, для
процессоров ARM-Cortex – M3/M4/M4F – 32 или 16 разрядов, что заметно облегчает
аппаратную организацию конвейера.
В более мощных процессорах, чем рассматриваемые в этой книге, например, в
многоядерных
ARM-процессорах,
сигнальных
процессорах
и
сигнальных
микроконтроллерах число этапов конвейера может увеличиваться до 8 и более. Это могут
быть специальные этапы, связанные с получением операндов из памяти данных и
сохранением в ней результата предыдущей операции.
Команда на конвейере будет находиться столько тактов, сколько
этапов имеет конвейер. Однако, с точки зрения наблюдателя на выходе
конвейера (пользователя), каждая команда будет выполняться за один такт.
Конвейер выполняет предварительную выборку команд из памяти и
последовательную их обработку, существенно повышая производительность
процессора.
В конвейерных архитектурах работа конвейера поддерживается несколькими
регистрами ЦПУ. Обычно это следующие регистры:
• Регистр – указатель адреса упреждающей выборки содержит адрес очередной
команды, которая будет считываться из кодовой памяти.
• Регистр команд – содержит код операции, подлежащий расшифровке в данный
момент.
• Регистр следующей команды в очереди – содержит код операции очередной команды в
очереди на дешифрацию.
• Программный счетчик PC – содержит адрес следующей, подлежащей выполнению
команды.
Если для процессора без конвейерной архитектуры предположить, что время
выполнения всех трех этапов (выборка, дешифрация, выполнение) одинаково и равно T,
то общее время выполнения команды будет равно tбез_конв= 3T. В процессорах с
конвейерной архитектурой при полностью заполненном конвейере время выполнения

57

ГЛАВА 4. ОСНОВНЫЕ ТИПЫ ПРОЦЕССОРНЫХ АРХИТЕКТУР
команды tконв= T. Выигрыш в производительности процессора равен 3. На самом деле он
несколько меньше (2.5 – 2.6) за счет дополнительных служебных издержек.
Если выполняется команда перехода или вызова подпрограммы, то конвейер
очищается («все команды с него смываются») – загрузка конвейера начинается с нового
адреса, как показано на рис. 4.3.
Дальнейшее совершенствование конвейерной архитектуры: предсказание условных
и безусловных переходов, в которых задержки, связанные с очисткой конвейера,
минимизируются.

4.5 Модифицированная Гарвардская архитектура
Любой современный микроконтроллер имеет процессор, встроенную память
программ и данных определенного размера, ограниченный набор встроенных
периферийных устройств. В большинстве практических применений список этих ресурсов
микроконтроллера является основанием для его выбора. Это так называемая «закрытая»,
нерасширяемая архитектура микроконтроллера. Однако, в ряде приложений возникает
необходимость при сохранении микроконтроллера либо расширить его память программ
или данных, либо подключить к нему какие-то другие периферийные устройства. Такие
микроконтроллеры являются расширяемыми и должны содержать шины для подключения
внешнего оборудования. Они должны иметь так называемую открытую архитектуру.
Но в Гарвардской архитектуре таких шин много. Сколько же потребуется
дополнительных выводов, чтобы реализовать эту технологию?
Компромисс может состоять в том, что вся внутренняя архитектура
микроконтроллера является настоящей многошинной Гарвардской, а для сопряжения с
внешними БИС памяти и внешними периферийными устройствами используются общие
шины адреса и данных и общая шина управления, содержащая сигналы чтения и записи, а
также сигналы выборки нужной области памяти (селектирования). Такая архитектура
получила название модифицированной Гарвардской архитектуры. Типичными
представителями этой архитектуры являются сигнальные процессоры и сигнальные
микроконтроллеры.
Наша книга посвящена изучению процессорных ядер ARM-Cortex-M3/M4/M4F,
реализованных на базе Гарвардской архитектуры. Поэтому, более сложные варианты
архитектур, используемые в высокопроизводительных процессорах, так называемые
архитектуры многоядерных процессоров не рассматриваются.
Процессоры с Гарвардской архитектурой используются внаиболее
часто
применяемых
на
практике
и
относительно
дешевых
микроконтроллерах с определенным объемом памяти и фиксированным
набором периферийных устройств (нерасширяемые МК). Процессоры с
модифицированной Гарвардской архитектурой применяются в более
сложных и дорогих микроконтроллерах, допускающих подключение дополнительной
памяти и периферии (расширяемые МК).
1) Может ли программа в процессорах с фон-Неймановской
архитектурой располагаться в ОЗУ?
2) А в процессорах с Гарвардской архитектурой?
3) Когда в процессорах с конвейерной архитектурой выполняется
полная «перезагрузка» конвейера?

58

ГЛАВА 4. ОСНОВНЫЕ ТИПЫ ПРОЦЕССОРНЫХ АРХИТЕКТУР
1) Может. Обычно это так и делается в процессе отладки. Программа
может загружаться в ОЗУ и выполняться из него также в процессе
эксплуатации (как в обычных компьютерах). В этом и состоит опасность
случайной или преднамеренной ее «порчи».
2) Теоретически – нет, именно в целях максимальной защиты
программы. На практике, как в процессорах фирмы ARM, разработчики
процессоров предусматривают возможность обмена данными («мост») между
интерфейсами памяти программ и памяти данных. Однако, в этом случае достичь
такой же производительности, как при выполнении программы из кодовой памяти,
невозможно. Этот режим используется только для отладки.
3) В том случае, когда последовательный ход программы нарушается, то есть при
выполнении условного или безусловного перехода в другую точку программы, а
также при вызове подпрограммы.

Список рекомендуемой литературы
1) Джозеф Ю. Ядро Cortex-M3 компании ARM. Полное руководство/ Ю. Джозеф; пер.
с англ. А.В. Евстафеева. – М.: Додэка-XXI, 2012. – 552 c. ил. (Мировая
электроника).
2) Козаченко В.Ф. Микроконтроллеры: руководство по применению 16-разрядных
микроконтроллеров Intel MCS-196/296 во встроенных системах управления. – М.:
ЭКОМ, 1997. – 688 с.
3) ARM DDI 0403C_errata_v3, ARMv7-M Architecture Reference Manual, Arm Limited,
2010.
4) ARM DDI 0337H, Cortex-M3. Technical Reference Manual. Arm Limited, 2010.
5) ARM DDI 0439D. ARM Cortex-M4 Processor. Technical Reference Manual. Arm
Limited, 2013.
6) Texas Instruments. SPMU 159a. Cortex-M3/M4F Instruction Set. Technical User’s
Manual. Texas Instruments Inc. – 2011.

59

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F

5 АРХИТЕКТУРА
ПРОЦЕССОРНЫХ ЯДЕР ARM
CORTEX-M3/4/4F
Оглавление
5.1 Общие сведения ..................................................................................................................... 60
5.2. Архитектура процессорного ядра Cortex-M4 .................................................................... 62
5.2.1. Операционный блок ЦПУ............................................................................................62
5.2.2. Периферийные устройства, встроенные в ЦПУ ........................................................64
5.2.3. Встроенные модули отладки ЦПУ .............................................................................64
5.2.4. Система шин .................................................................................................................66
5.2.4.1. Интерфейс выборки инструкций из кодовой памяти ICode ............................. 67
5.2.4.2. Интерфейс выборки данных из кодовой памяти DCode................................... 68
5.2.4.3. Системный интерфейс (System interface) ........................................................... 68
5.2.4.4. Собственная периферийная шина ЦПУ ............................................................. 68
5.2.5. Опциональные модули процессора Cortex-M4 ..........................................................70
5.3. Основные технические характеристики ядра Cortex-M4 ................................................. 71
5.4. Режимы работы процессора ................................................................................................ 72
5.5. Операционные состояния процессора ................................................................................74

5.1 Общие сведения
Процессорные ядра Cortex-M3/M4/M4F имеют во многом общую
архитектуру и систему команд. Поэтому дальнейшее изложение будем
вести на примере самого мощного ядра Cortex-M4F. Отличительные
особенности процессорных ядер представлены в табл. 5.1.
Таблица 5.1 Особенности ядер Cortex-M
Ядро

Особенности

M3

Ядро с Гарвардской архитектурой и трехступенчатым конвейером. Улучшенная
базовая RISC-архитектура – ARMv7-M. Расширенная система команд, аппаратная
поддержка умножения и деления. Cистема команд оптимизирована для типовых
микроконтроллерных применений. Возможность замены обычных 8- и 16-разрядных
микроконтроллеров с существенным повышением производительности за счет
перехода к 32-разрядным операциям. Разработка программного обеспечения на
Ассемблере и на языке высокого уровня С/С++.

60

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F
M4

M4F

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

Процессорные ядра семейства Cortex-M разработаны фирмой ARM для
использования в качестве ядер высокопроизводительных микроконтроллеров,
представляющих собой законченные системы на кристалле System-on-Chip (SoC),
ориентированные на разные области встроенного управления оборудованием. Фирмапроизводитель конечных изделий (микроконтроллеров) самостоятельно добавляет к
процессорному ядру память программ, память данных и определенный набор
периферийных устройств, ориентированный на предполагаемого потребителя. Фирма
ARM, со своей стороны, гарантирует полную работоспособность процессорного ядра
при условии, что все интерфейсы и протоколы обмена с дополнительными компонентами
микроконтроллера будут соответствовать спецификациям фирмы.
Процессорное ядро является полностью 32-разрядным и имеет конвейерную RISCархитектуру, рассчитанную на выполнение большинства команд за один такт, и низкое
энергопотребление, что особенно важно во встраиваемых системах управления. Шины
адреса и данных 32-разрядные, как и все регистры общего назначения. Тем не менее,
поддерживается обработка данных в более коротких форматах – полуслово (16 разрядов),
байт (8 разрядов) и даже бит (1 разряд). Явного «битового сопроцессора» в ядрах ARM
Cortex-M нет. Однако, его функции эффективно поддерживаются общей системой
команд и специальными областями побитово/побайтово-адресуемой памяти данных и
периферийных устройств, так называемыми, битовыми лентами в памяти («семафорной»
памятью). Технология bit-banding (битовых лент) подробно рассмотрена в книге. Доступ к
биту в такой ленте выполняется всего лишь одной командой.
Одним из преимуществ процессорных ядер Cortex-M является интеграция на
кристалл центрального процессора важнейших периферийных устройств, необходимых
в каждом микроконтроллере для управления в реальном времени:
• Контроллера векторных приоритетных прерываний с минимизированными
задержками на обслуживание прерываний;
• Системного таймера;
• Контроллера выхода процессора из режимов малого энергопотребления.
Конечные пользователи (разработчики прикладных систем) могут сами
разрабатывать или покупать готовые операционные системы реального времени и
мониторы (упрощенные операционные системы – специализированные программы
верхнего уровня управления, рассчитанные на конкретную специфику области
применения), использующие системные периферийные устройства. При этом
обеспечивается независимость этих программных продуктов от объемов памяти и наборов
периферийных устройств в конкретных типах микроконтроллеров.
Процессор имеет мощную встроенную систему отладки, значительно
облегчающую и ускоряющую разработку программного обеспечения, конкретный состав
блоков которой зависит от требований производителя микроконтроллера. Все
современные стандарты интерактивной отладки, в том числе с использованием
компьютерных интегрированных сред разработки, поддерживаются на аппаратном уровне
процессорного ядра.

61

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F
Особая гордость разработчиков – поддержка в Cortex-M4 команд цифровой
обработки сигналов в формате с фиксированной точкой, а в Cortex-M4F – аппаратная
интеграция в ЦПУ сопроцессора вычислений с плавающей точкой однократной точности
со своей мощной системой команд, большинство из которых, в соответствии с RISCтехнологией процессора, также выполняются за один такт. Эта опция переводит любой
микроконтроллер
с
данным
процессорным
ядром
в
разряд
сигнальных
высокопроизводительных микроконтроллеров, причем, по предельно низкой цене и при
минимальном уровне потребления энергии.
Как и любой RISC-процессор, ядра Cortex-M имеют систему команд,
оптимизированную для использования языков высокого уровня и соответствующих
компиляторов. Система команд процессоров настолько мощная, что, зачастую,
реализовать нужную функцию на Ассемблере даже проще, чем на Си. Особенно это
касается сопроцессора вычислений с плавающей точкой. Мы познакомим читателя с
этими уникальными возможностями, позволяющими создавать предельные по
быстродействию программы управления оборудованием.
Основная область применения процессорных ядер Cortex-M – системы реального
времени управления, в том числе двигателями, силовыми преобразователями энергии,
роботами, узлами автомобильной техники, распределенными системами автоматизации
технологических процессов, медицинским оборудованием и т.п.

5.2 Архитектура процессорного ядра Cortex-M4
Упрощенная блок-схема процессора Cortex-M4 изображена на рис. 5.1.
Предполагается, что в состав операционного блока включен опциональный модуль
поддержки вычислений в формате с плавающей точкой FPU (ядро Cortex-M4F).

5.2.1 Операционный блок ЦПУ
1) Арифметико-логическое устройство (АЛУ) для выполнения арифметических,
логических и других операций над 32-разрядными операндами с дополнительным
кольцевым сдвиговым регистром, позволяющим выполнять так называемые
«попутные» операции сдвига при выполнении большинства обычных операций.
2) Аппаратный умножитель 32-разрядных чисел с возможностью получения 64-битного
результата.
3) Аппаратный делитель 32-разрядных чисел.
4) Файл регистров общего назначения (РОН) r0-r15, каждый из которых может быть
источником или приемником операции.
5) Сопроцессор для обработки чисел в формате с плавающей точкой – модуль FPU Floating Point Unit.
6) Файл регистров сопроцессора s0-s31, каждый из которых может быть источником или
приемником операции с плавающей точкой.
Один из регистров общего назначения играет особую роль (R15), являясь
счетчиком команд PC, содержимое которого задает адрес очередной команды в кодовой
памяти, подлежащей выполнению.

62

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F

Процессорное ядро Cortex-M4

Порт
трассировки

Операционный блок ЦП
Модуль точек
наблюдения и
трассировки
данных (DWT)
Встроенная
макроячейка
трассировки
(ETM)

0XE004 0000

Сдвиговый
регистр

0xFFFF FFFF

Арифметикологическое
устройство - АЛУ
Аппаратный
умножитель

Регистры
общего
назначения:
R0-R15
Счетчик
команд PC
(R15)

Аппаратный
делитель

0E003 FFFF

Встроенные в ЦПУ
переферийные устройства

Контроллер
вложенных векторных
прерываний (NVIC)
Контроллер выхода
из режима малого
потребления
(WIC)
0XE000 0000

Системный
таймер
(Sys Timer)

SW JTAG-DP

Модуль
плавающей
точки (FPU)

Интерфейс
встроенных в ЦПУ
периферийных
устройств

Область
произв.-ля
0xE010 0000

Интерфейс
системы

Макроячейка
инструментальной
трассировки
(ITM)

Интерфейс системы отладки
частная периферийная шина
Private Peripheral Bus (PPB)

Матрица шин
(Bus Matrix)

Модуль точек
останова во
флэш-памяти
(FPB)

Порт отладки

Встроенные в ЦПУ
модули поддержки
отладки (Debug)

Модуль защиты памяти
Memory Protection Unit (MPU)

0XE00F FFFF

SW-DP

3-х уровневый
конвейер команд

Внешние
ПУ
0xA000 0000
0x9FFF FFFF

Внешнее
ОЗУ
0x6000 0000
0x5FFF FFFF

Регистры
модуля FPU:
S0-S31

Дешифратор
команд

0xDFFF FFFF

Встроен.
ПУ
Интерфейс
данных в
кодовой
памяти
DCode
Интерфейс
команд
ICode

0x4000 0000
0x3FFF FFFF

ОЗУ
SRAM
0x2000 0000
0x1FFF FFFF

Память
программ
Code
0x0000 0000

Рис. 5.1 Структура процессорного ядра Cortex-M4

63

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F

5.2.2 Периферийные устройства, встроенные в ЦПУ
Мы уже отмечали, что одним из преимуществ процессорных ядер Cortex-M
является интеграция непосредственно в процессор ряда важнейших периферийных
устройств:
1) Контроллера вложенных векторных прерываний Nested Vectored Interrupt Controller
(NVIC).
2) Системного таймера (Sys Timer).
3) Контроллера выхода из режима малого потребления энергии (WIC).
Интеграция такой периферии непосредственно в ЦПУ позволяет предельно
минимизировать затраты времени на обращение к этим устройствам, в том числе, время
реакции на прерывания, что является очень важным для систем управления реального
времени.
С использованием встроенной периферии процессорного ядра специализированные
фирмы разрабатывают и продают конечным пользователям так называемое программное
обеспечение верхнего уровня (мониторы, операционные системы реального времени и
т.д.). Это ПО может работать совместно с пользовательскими программами, являющимися
программным обеспечением нижнего уровня, что позволяет создавать, так называемые,
многозадачные приложения, в которых системы верхнего уровня координируют запуск и
выполнение нескольких задач прикладного уровня.

5.2.3 Встроенные модули отладки ЦПУ
На современном этапе развития микропроцессорной техники базовые средства
поддержки отладки интегрируются непосредственно на кристалл микроконтроллера или
процессора. Фирма ARM не передает эту важную функцию разработчикам
микроконтроллеров, а реализует ее сама в архитектуре процессорного ядра. Такой подход
позволяет унифицировать все отладочные средства, методы отладки и отладочные
интерфейсы любых микроконтроллеров, реализованных на процессорных ядрах ARM
любыми производителями.
С их помощью поддерживаются:
1) Доступ в режиме отладки ко всей памяти системы и регистрам процессора, включая
доступ к регистрам периферийных устройств;
2) Установка в отлаживаемой программе точек останова, при достижении которых
выполнение программы приостанавливается и управление передается отладчику.
Программист имеет возможность оценить промежуточные результаты работы
программы, изучить состояние памяти и регистров процессора;
3) Установка точек наблюдения, при достижении которых значения переменных
сохраняются для последующего анализа.
4) Трассировка программы, когда она выполняется либо в пошаговом режиме, либо от
одной точки останова до другой с возможностью контроля значений переменных.
5) Профилирование программы. Оценка быстродействия (времени выполнения) всей
программы или отдельных ее фрагментов (подпрограмм), частоты обращения к
подпрограммам с целью последующей оптимизации наиболее часто используемых
фрагментов кода или определения вообще не используемых участков кода.
6) Вывод отладочной информации на дисплей системы верхнего уровня (компьютер),
используемый для отладки.
7) Сопряжение с анализаторами данных в порту трассировки Trace Port Analyzer (TPA).

64

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F
8) Доступ к встроенным в процессор отладочным устройствам через два внешних
интерфейса, к которым подключаются компьютерные системы отладки верхнего
уровня:
o Последовательный отладочный JTAG-порт Serial Wire JTAG Debug Port (SWJDP), являющийся стандартом, де-факто, для производителей отладочного
оборудования и микроконтроллеров;
o Последовательный отладочный порт Serial Wire Debug Port (SW-DP), широко
используемый производителями ARM-процессоров и микроконтроллеров;
o В конкретном микроконтроллере может быть либо один из этих интерфейсов,
либо оба для сопряжения со средствами отладки разных производителей.
o Часто JTAG-порт является универсальным и совмещается с SW-DP-портом.
При выборе микроконтроллера убедитесь в том, что по крайней мере
один из указанных выше отладочных портов имеется на кристалле. Только в
этом случае Вы будете иметь шанс на полноценную отладку своего
программного обеспечения.
Конечному пользователю не требуется детальное знание специфики каждого
отдельного отладочного модуля, входящего в процессорное ядро, в том числе протоколов
обмена этих модулей с ЦПУ. Эта информация – для разработчиков и производителей
микроконтроллеров (поставляется вместе с лицензией на процессор). Тем не менее, при
выборе микроконтроллера желательно поинтересоваться, какие из возможных отладочных
модулей включены в состав процессора при покупке лицензии у фирмы ARM.
Отмеченные выше отладочные функции поддерживаются несколькими
модулями отладки, которые могут быть заказаны разработчиками
микроконтроллеров в качестве опций при покупке лицензии на процессор
(см. табл. 5.2).
Таблица 5.2 Набор отладочных функций
Назначение модуля

Возможные варианты
комплектации

DWT

Data Watch point and Trace Unit – Модуль точек
наблюдения и трассировки программы
Instrumentation Trace Macrocell Unit –Макро-ячейка
(модуль) инструментальной трассировки для вывода
данных
вместо
интерфейса
JTAG
через
порт
трассировки, что быстрее
Embedded Trace Macrocell – Макро-ячейка встроенной
трассировки
AHB Trace Macrocell interface – Интерфейс макро-ячейки
трассировки
Trace Port Interface Unit – Модуль интерфейса порта
трассировки для вывода трассировочной информации
Debug Port AHB-AP interface – Интерфейс отладочного
порта AHB-AP

-

ITM

ETM
HTM
interface
TPIU
Debug
Port

-

-

Трассировка не поддерживается

Модуль
отладки

˅

˅

˅

˅

˅

˅

˅

˅

-

˅

˅

˅

-

-

˅

˅
˅

˅

˅

˅

˅

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

65

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F
Для того, чтобы внешняя аппаратура имела доступ к встроенным в
процессор средствам отладки, в составе отладочных модулей процессора
должен обязательно присутствовать модуль порта отладки Debug Port.
Именно через него обеспечивается подключение внешних отладчиков,
управление отладкой и доступ к памяти и регистрам процессора даже во
время работы пользовательской программы – «на лету».
Обычно управление отладкой и получение всей отладочной информации
выполняется через порт отладки (например, по интерфейсу JTAG). Если в
микроконтроллере будет дополнительно присутствовать порт трассировки, то к нему
могут быть подключены специальные устройства – трассировщики, собирающие и
быстро выводящие отладочную информацию на экран компьютера. Это дополнительные
аппаратные средства ускорения отладки.
В книге мы познакомим читателя с важнейшими приемами отладки, которые
предоставляют современные интегрированные среды разработки, выполняющие свои
функции во взаимодействии со встроенными в процессор модулями отладки.
Для сложных проектов требуется отладка в реальном времени, когда на фоне
выполнения пользовательской программы, без остановки работы всех встроенных в
процессор периферийных устройств, выполняется наблюдение за состоянием тех или
иных переменных процесса, возможно, с графическим выводом результатов трассировки
на экран отладчика. Для этой цели необходимы дополнительные внешние программные
и/или аппаратные средства, описание которых выходит за рамки этой книги.

5.2.4 Система шин. Сопряжение процессора с другими модулями
микроконтроллера
Процессорные ядра Cortex-M имеют Гарвардскую архитектуру, отличающуюся
раздельными областями памяти для хранения программного кода и данных – рис. 5.1.
Шина адреса для доступа к памяти и периферийным устройствам 32-разрядная. Все
имеющееся адресное пространство проектировщиками процессора жестко разделено на
области, в каждой из которой могут адресоваться только определенные устройства: 1)
память программ (кодовая); 2) память данных – статическое ОЗУ; 3) регистры
периферийных устройств, интегрированных на кристалл микроконтроллера фирмойпроизводителем микроконтроллера; 4) внешнее по отношению к микроконтроллеру ОЗУ;
5) внешние периферийные устройства, подключаемые к микроконтроллеру разработчиком
конкретной (прикладной) микропроцессорной системы; 6) область памяти производителя
микроконтроллера, в которой он может разместить любые дополнительные устройства по
своему усмотрению.
В соответствии с поддерживаемой концепцией отображения адресов всех
регистров периферийных устройств, встроенных в микроконтроллер на область памяти,
определенные диапазоны адресов выделены и для регистров встроенных в процессор
периферийных устройств (для системной периферии), а также для регистров модулей
поддержки отладки. На рис. 5.1 для удобства читателей для каждого из блоков памяти и
периферии указаны начальные и конечные адреса, в соответствии с принятым фирмой
ARM унифицированным распределением памяти.
Процессор Cortex-M4 разработан в соответствии с архитектурой ARMv7E-M для
микроконтроллерных применений и имеет три основных интерфейса:
1) ICode memory interface – для выборки кодов команд (I – instruction) из кодовой
памяти;
2) DCode memory interface – для выборки данных (D – Data) из кодовой памяти;
3) System interface – системный интерфейс со встроенной памятью и периферийными
устройствами микроконтроллера;
66

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F
Назначение первого интерфейса – выборка кодов команд из памяти программ и
загрузка их на конвейер команд для расшифровки и выполнения.
Назначение третьего интерфейса – доступ процессора к переменным,
расположенным в оперативной памяти как по записи, так и чтению. Так как регистры всех
периферийных устройств «отображены на память», то для доступа к памяти данных и
периферийным устройствам используется одна общая шина. Одновременный доступ к
данным в оперативной памяти и к периферийным устройствам не поддерживается.
Назначение второго интерфейса требует комментария. Дело в том, что в
процессорах ARM используются как 32-разрядные шины адреса, так и 32-разрядные
шины данных. Более того, сами команды являются 32-разрядными. В этой ситуации
проблемой оказывается загрузка внутренних регистров ЦПУ константами
(непосредственными данными). Константы тоже 32-разрядные. Как разместить их в 32разрядной команде? Понятно, что это невозможно. Разработчики архитектуры ARM
предложили оригинальное решение:
• Предусмотрели механизм компрессии (сжатия) констант, когда некоторая группа
констант все же может разместиться в 32-разрядной команде (в 10 или 12 битах);
• Для всех же остальных констант предусмотрели механизм автоматического
размещения их транслятором в свободных областях кодовой памяти с последующим
доступом к ним с использованием относительной адресации по текущему значению
счетчика команд PC и смещению (далее подробно в 9.3).
Именно для считывания таких, расположенных в кодовой памяти данных, и
предусмотрен интерфейс DCode. Он автоматически начинает работать, когда встречается
команда загрузки данных с относительной адресацией по счетчику команд. Более того,
считывание данных выполняется настолько быстро, что команда-инициатор этого
процесса (загрузки регистра константой), получив нужное значение из памяти,
выполняется за один цикл, как и положено в процессорах с RISC-архитектурой. Можно
сказать, что считывание кода операции из кодовой памяти выполняется параллельно со
считываем из нее данных.
Все три внутренние шины выполнены в одном стандарте, разработанном фирмой
ARM

Advanced
High-performance
Bus
(AHB)-Lite

расширенной
высокопроизводительной шины (AHB), упрощенной (Lite).
Четвертая шина Advanced Peripheral Bus (APB) – расширенная периферийная шина
предназначена для интерфейса встроенной в процессор системы отладки с внешним
отладочным оборудованием. Это обеспечивает независимую работу системы отладки от
работы программы пользователя (отладку «на лету»).
Как видите, в процессоре Cortex-M4 полностью реализованы преимущества
Гарвардской архитектуры, когда выборка команд может выполняться параллельно с
выборкой данных по независимым шинам (как из кодовой памяти, так и из памяти
данных), что и обеспечивает высокую производительность процессора.
5.2.4.1 Интерфейс выборки инструкций из кодовой памяти ICode
Обеспечивает доступ к кодам команд только в кодовой памяти (Code memory) в
диапазоне адресов с нулевого адреса 0x00000000 по 0x1FFFFFFC (адреса последнего 32разрядного слова в зоне кодовой памяти). Выборка кодов команд (инструкций) из этой
области выполняется исключительно 32-разрядными словами.
Реальное количество кодов команд, извлеченных из кодовой памяти за один
доступ, зависит от длины команд: поддерживается доступ как к 32-разрядным командам
набора ARM, так и к 16-разрядным командам набора Thumb-2. В процессорах CortexM3/M4/M4F используется единая, общая система команд, объединяющая достоинства как
32-разрядных, так и 16-разрядных команд. Если команды 16-разрядные, то возможно
считывание из программной памяти на конвейер сразу двух команд.
67

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F
Процессор имеет 3-уровневый конвейер команд, на который и поступают
извлеченные из памяти команды. Принцип работы конвейера команд подробно описан
нами ранее (см. 4.4).
5.2.4.2 Интерфейс выборки данных из кодовой памяти DCode
В кодовой памяти могут располагаться не только коды инструкций, но и данные –
константы и таблицы констант. Очень часто здесь находятся 32-разрядные адреса меток
или точек входа в подпрограммы. По мере выполнения программы адреса должны
считываться и загружаться в регистры процессора (должна выполняться инициализация
32-разрядных регистров-указателей).
Данные в кодовой памяти могут быть не только словами, но и полусловами и
байтами. Поэтому, диапазон адресов доступа к данным в кодовой памяти 0x00000000 –
0x1FFFFFFF (адрес последнего байта). Всего в кодовой памяти 0,5 Гбайт.
Доступ к данным в этой области может запрашивать как процессорное ядро, так и
отладочная система. Реально, при считывании байта или полуслова доступ всегда
выполняется к полному 32-разрядному слову.
Считывание данных из кодовой памяти имеет более высокий приоритет по
сравнению со считыванием кодов команд ICode, так как данные нужны для уже
считанных и выполняемых в данный момент команд. Например, для команд доступа к
данным с использованием относительной адресации по счетчику команд. Далее мы
рассмотрим технологию доступа к так называемым «литеральным пулам» данных
(константам) в кодовой памяти более подробно (см. 9.3).
5.2.4.3 Системный интерфейс (System interface)
Через этот 32-разрядный интерфейс обеспечивается доступ к следующим областям
памяти микроконтроллера:
• 0x20000000 – 0xDFFFFFFF – область памяти встроенного в микроконтроллер
статического ОЗУ, встроенной периферии, внешнего ОЗУ, внешних периферийных
устройств.
• 0xE0100000 – 0xFFFFFFFF – область памяти, назначение которой определяет
разработчик микроконтроллера.





Интерфейс системной шины содержит специальную логику, которая обеспечивает:
Возможность не выровненного по границе 32-разрядного слова доступа к данным;
Доступ к специальным областям по-битово-/по-байтово- адресуемого ОЗУ (к так
называемым «битовым лентам») по 32-разрядным «псевдоадресам» битов;
Выборку кодов команд из ОЗУ с последующей загрузкой на конвейер команд;
Доступ к данным в памяти или регистрам периферийных устройств, запрошенным
системой отладки.

Обратите особое внимание на то, что программный код можно при отладке
размещать не в программной памяти, (например, во встроенной флэш-памяти), а в ОЗУ.
Такую программу можно выполнить и отладить, но время ее выполнения будет несколько
больше, так как системная шина не оптимизирована для работы с программными кодами
(в частности нет отдельной шины считывания данных).
5.2.4.4 Собственная периферийная шина ЦПУ
Для того, чтобы обеспечить предельное быстродействие и минимизацию
временных задержек при работе со встроенными в процессор модулями отладки, они
подключаются к нему по отдельной расширенной высокопроизводительной шине

68

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F
Advanced High-performance Bus (AHB), которая называется собственной периферийной
шиной процессора – Private Peripheral Bus (PPB).
Все отладочные модули, являющиеся, как и процессор, собственной разработкой
инженеров ARM, имеют оптимизированный по быстродействию протокол обмена,
отличающийся от стандартного протокола обмена данными с внешними периферийными
устройствами (протокола шины системного интерфейса). Эти устройства в области
памяти от 0xE004 0000 до 0xE00F FFFF должны быть полностью совместимы с ядром
процессора (Core-compatible). Поэтому обычно поставляются вместе с процессором. При
обмене данными с ними имеются некоторые специальные ограничения, которые
нежелательно использовать при работе с обычными периферийными устройствами,
поставляемыми производителями микроконтроллеров.
Все
собственные
периферийные
устройства
процессора,
подключенные к шине PPB в диапазоне адресов (от 0xE004 0000 до 0xE00F
FFFF), могут быть доступны только в привилегированном режиме работы
процессора (privileged mode) – см. 5.4. Доступ к ним выполняется только
программами-отладчиками, создаваемыми специализированными фирмами.
Обычные пользователи имеют доступ к этим устройствам только опосредованно – через
купленные ими аппаратно-программные средства поддержки отладки.
Доступ к системным периферийным устройствам, таким, как контроллер
прерываний, также выполняется по периферийной шине процессора. Отличие состоит в
том, что он может выполняться и в непривилегированном режиме. Это связано с тем, что
контроллер прерываний широко используется практически всеми разработчиками
конечных изделий в любой прикладной программе и, поэтому, должен быть максимально
доступен.
5.2.4.5 Матрица шин
Вы уже обратили внимание на сложность шинной организации процессора,
например, на то, что программа может выполняться из оперативной памяти. Как
достигается такая универсальность? За счет использования своеобразного шинного
мультиплексора (переключателя) – матрицы шин (Bus Matrix). Она позволяет направить
поток данных в нужное в данный момент устройство, выполняя функцию шинного
соединителя.
Итак, каждая шина по физической структуре и протоколу обмена оптимизирована
для выполнения своих специализированных операций, а мостом между ними является
матрица шин, которая выполняет не только физическое соединение шин между собой, но
и согласовывает протоколы обмена данными по этим шинам.
1) Почему процессорные ядра Cortex-M относятся к процессорам с
Гарвардской архитектурой?
2) Вспомните, какие этапы имеет 3-уровневый конвейер команд?
3) Какие операции в процессорных ядрах Cortex-M могут выполняться
параллельно?
4)
В чем преимущество процессорных ядер ARM, содержащих внутри
системную периферию и модули поддержки отладки?
5) Какие области памяти не обслуживаются системным интерфейсом?
1) Они имеют физически разные области памяти для хранения кодов
команд и данных и физически разные шины для доступа к ним,
оптимизированные по быстродействию и протоколам обмена.
2) Выборки команды, ее дешифрации, выполнения.

69

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F
3) Операции считывания кода команды из кодовой памяти с загрузкой ее на конвейер;
декодирования ранее выбранной команды; выполнения уже декодированной команды,
в том числе с доступом к переменным в оперативной памяти или к регистрам
периферийных устройств. Возможно также параллельное обращение за данными,
расположенными в кодовой памяти – по шине DCode.
4) Системное программное обеспечение мало зависит от частоты работы конкретного
микроконтроллера, объема встроенной в него памяти и набора встроенных
периферийных устройств. Оно сохраняется, например, при переходе на более
производительный микроконтроллер с большим объемом памяти или отличающийся
другой периферией.
5) Это область памяти программ и область памяти, отведенная под собственные
периферийные устройства процессора – контроллер прерываний, системный таймер,
модули поддержки отладки (адресный диапазон 0xE000 0000 – 0xE00F FFFF).

5.2.5 Опциональные модули процессора Cortex-M4
Некоторые блоки процессора Cortex-M4 являются опциями и поставляются по
требованию производителя микроконтроллера:











Опциональный модуль защиты памяти Memory Protection Unit (MPU),
предназначенный для защиты определенных областей памяти от случайного или
несанкционированного доступа. Используется только в достаточно сложных системах,
как правило, имеющих операционную систему реального времени. Области памяти,
которые использует программное обеспечение верхнего уровня, могут быть
заблокированы для доступа со стороны обычных пользовательских программ, что
повышает надежность работы всей системы.
Опциональный модуль обработки чисел в формате с плавающей точкой однократной
точности Floating Point Unit (FPU). Одна из важнейших опций, существенно
расширяющих вычислительные возможности и производительность процессора.
Дальше рассматривается подробно (см. 20.1).
Модуль Flash Patch Breakpoint (FPB) – установки точек останова во встроенной
флэш-памяти микроконтроллера и локальных вставок («заплаток») во флэш-памяти.
Модуль выхода процессора из режимов малого потребления энергии Wake-up Interrupt
Controller (WIC) – контроллер «пробуждающих» прерываний. Позволяет отключать
питание процессора с сохранением его состояния и быстро возвращать в работу при
возникновении прерывания.
Debug Port – Интерфейс отладочного порта AHB-AP.
Bit-banding – Аппаратная поддержка «битовых лент» в памяти данных с
возможностями доступа индивидуально к каждому биту, ориентированная на
эффективную обработку бит, фактически поддержка «битового сопроцессора».
Constant AHB control – Средства постоянного контроля шин AHB.

Прежде
чем
выбрать
для
применения
определенный
микроконтроллер, конкретной фирмы-производителя убедитесь в том, что
его процессорное ядро Cortex содержит нужные Вам опции.
Если Вы используете микроконтроллер с ядром Cortex-M4F, то это
не значит, что модуль поддержки вычислений с плавающей точкой готов к
работе «по умолчанию». Его работу необходимо предварительно
разрешить, как и любого сопроцессора. Это же относится к любым опциональным
модулям. Далее мы покажем, как выполняется инициализация процессора –
устанавливается нужный режим работы и конфигурируется его окружение.
70

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F

5.3 Основные технические характеристики ядра Cortex-M4
Ядро Cortex-M4 core выполняет функции быстродействующего центрального
процессора со следующими возможностями:
1) Единый набор команд Thumb, разработанный для архитектуры процессоров фирмы
ARM®v7-M, объединяющий преимущества двух ранее существовавших наборов
команд: 32-разрядного набора команд ARM и 16-разрядного набора Thumb-2.
Обеспечение более высокого быстродействия и одновременно более высокой
плотности кода. Автоматический выбор более компактной команды в процессе
ассемблирования программы;
2) Работа с операндами, расположенными исключительно в регистрах ЦПУ R0-R15;
3) Выполнение большинства команд за один такт.
4) Основной пользовательский режим работы Thread (или User Mode) и режим
обработчика
исключений
Handler
Mode,
в
котором
может
работать
специализированное системное ПО типа операционной системы реального времени;
5) Два состояния процессора: выполнение команды единого набора Thumb и состояние
отладки Debug, в котором обеспечивается доступ отладчика ко всем ресурсам;
6) Аппаратный умножитель 32-разрядных чисел в формате с фиксированной точкой с
поддержкой операций умножения-накопления;
7) Аппаратный делитель целых чисел без знака и со знаком;
8) Банк из двух указателей стека Stack Pointer (SP) для разных режимов работы
процессора (пользовательского и обработки исключений). Возможность работы
системного и пользовательского ПО с разными стеками для повышения надежности;
9) Автоматическое сохранение и восстановление состояния процессора при обработке
прерываний с минимальными задержками вызова процедур обработки прерываний и
возврата в основную программу;
10) Поддержка прерываемых операций множественной пересылки данных из регистров в
память и обратно (команды LDM, STM, PUSH и POP) с минимальными задержками
входа и выхода в/из процедур обслуживания прерываний Interrupt Service Routine
(ISR);
11) Поддержка двух вариантов расположения байт в словах: прямого и обратного;
12) Поддержка не выровненного доступа к байтам для более эффективного использования
памяти данных.
Модуль аппаратной поддержки вычислений в формате с плавающей точкой
Floating Point Unit (FPU) выполняет функцию сопроцессора и обеспечивает:
1) Обработку операндов, расположенных в 32-разрядных регистрах модуля плавающей
точки S0-S32, которые представляют собой дополнительное сверхоперативное ОЗУ
модуля FPU. Регистры S0-S31 могут быть доступны и как шестнадцать 64-разрядных
регистров, содержащих двойные слова (double-word);
2) Операции обработки 32-разрядных чисел с плавающей точкой однократной точности:
преобразования форматов, сложения, вычитания, сравнения, умножения, умножения с
накоплением, деления, извлечения квадратного корня;
3) Операции обычного последовательного умножения с накоплением (Multiply and
Accumulate) и одновременного умножения с накоплением повышенной точности –
(Fused MAC);
4) Расширение диапазона чисел с плавающей точкой однократной точности за счет
поддержки операций с денормализованными (denormals) числами и всех режимов
округления, предусмотренных международным стандартном IEEE 754.
71

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F
5) Извлечение и выполнение команд обработки чисел с плавающей точкой на
трехстадийном конвейере с независимой работой стадий конвейера.
6) Выполнение большинства команд сопроцессора за один такт.
Контроллер вложенных векторных прерываний Nested Vectored Interrupt
Controller (NVIC) полностью интегрирован в ядро процессора для предельного
уменьшения задержек в обслуживании прерываний. Он обеспечивает:
1) До 240 внешних запросов прерываний с номерами от 1 до 240;
2) Поле битов приоритетов прерываний размером 3÷8 бит, позволяющее задать от 7 до
256 уровней прерываний, соответственно;
3) Динамическую смену приоритетов прерываний – (dynamic reprioritization);
4) Группировку запросов прерываний по приоритетам (priority grouping) для выделения
групп прерываний, имеющих преимущественный приоритет, и групп прерываний, не
имеющих преимущественного приоритета;
5) Автоматическое сохранение контекста при вызове процедуры обработки прерывания и
восстановление при возврате из нее;
6) Поддержку последовательной обработки нескольких запросов прерываний (цепочки
запросов) без дополнительных затрат времени на сохранение и восстановление
контекста.
7) Опциональный контроллер прерываний «с будильником» Wake-up Interrupt Controller
(WIC), который обеспечивает ультранизкий уровень потребления питания в режиме
«сна» микроконтроллера (sleep mode) и «пробуждение» по сигналу запроса
прерывания.
Принципиальным отличием процессоров ARM от многих других
процессоров является интеграция на кристалл процессора контроллера
прерываний, который обрабатывает не только запросы прерываний от
внешних источников, встроенных и внешних периферийных устройств, но
и исключения, которые могут возникнуть при некорректном выполнении
программного кода, таком как попытка выполнения несуществующей
операции или делении на ноль. Эти возможности позволяют существенно повысить
надежность программного обеспечения, особенно в цифровых системах управления
ответственным оборудованием.
Для поддержки обработки прерываний и исключений, в памяти процессора должна
располагаться таблица векторов перехода на процедуры обработки прерываний и
исключений. Организация этой таблицы в процессорах Cortex-M с указанием источников
прерываний/исключений рассматривается в следующей главе. Место расположения этой
таблицы в памяти строго определено архитектурой процессора.
Прежде, чем написать даже простенькую прикладную программу, нужно, как
минимум, инициализировать процессор и, по крайней мере, начальную часть таблицы
векторов прерываний/исключений. Об этом – следующая глава.

5.4 Режимы работы процессора. Привилегии доступа к
регистрам специального назначения
Процессор поддерживает два возможных режима работы: 1) Пользовательский –
User mode, называемый также режимом выполнения потока команд, потоковым режимом
– Thread mode и 2) Обработчика исключений – Handler mode. В первом режиме обычно
выполняется любая программа приложения. Во второй режим работы процессор

72

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F
переходит только при обнаружении исключения и автоматической передачи управления
соответствующему обработчику исключения.
Эти режимы отличаются тем, какие привилегии предоставляются программному
коду в отношении доступа к специальным регистрам процессора (управляющим работой
собственно процессорного ядра и встроенной в процессор периферией).
Различают:
• Привилегированный уровень доступа (Privileged access level) – все ограничения на
доступ к системным регистрам процессора снимаются и за все последствия отвечает
программист, который в этом режиме должен действовать исключительно
внимательно и ответственно, как системный администратор компьютера. В этом
режиме доступа выполняется инициализация или переинициализация процессора и
встроенных периферийных устройств.
• Непривилегированный уровень доступа (Unprivileged access level) – такой, когда код
программы не может получить доступ к некоторым специальным регистрам
процессора, которые отвечают за функционирование ядра процессорной системы.
Такой уровень доступа исключаетвозможные плачевные последствия при
неправильном программировании со стороны неквалифицированного программиста.
Непривилегированный код содержат обычно программы конечных пользователей,
которые решают свои конкретные прикладные задачи (ПО нижнего уровня). Впрочем,
любой, написанный прикладным пользователем, «обработчик исключения», будет
выполняться уже в привилегированном режиме. Программное обеспечение верхнего
уровня, если оно присутствует в конкретном приложении (например, монитор или
операционная система реального времени), обычно выполняется в привилегированном
режиме, имея доступ ко всем ресурсам процессора.
Напомним, что исключения – это нестандартные ситуации, на которые реагирует
процессорное ядро, вызывая соответствующие обработчики (подпрограммы). Типичный
пример – случайное считывание кода команды из области памяти, которая предназначена
для регистров периферийных устройств, где программный код находиться просто не
может.




Как происходит управление режимами работы процессора?
Процессор переходит в пользовательский режим работы (User Mode) сразу после
сброса (Reset) или в результате возврата из процедуры обработки исключения. В этом
режиме может выполняться как привилегированный (Privileged), так и
непривилегированный (Unprivileged) код. Сразу после сброса процессора «по
умолчанию» устанавливается привилегированный режим доступа, чтобы
пользователь имел возможность проинициализировать процессор в соответствии со
спецификой своего приложения.
Процессор переходит в режим обработчика исключений (Handler mode) только в
результате возникновения исключения и передачи управления соответствующему
обработчику. Весь код в этом режиме работы является только привилегированным
(Privileged). В процедуре обслуживания исключения, в самом ее конце, последней
командой процессор переводится в режим нормальной работы – в пользовательский
режим User Mode.

Графическая иллюстрация возможных режимов работы процессора и допустимых
переходов из одного режима в другой показана на рис. 5.2.
Режим обработки исключений Handler Mode всегда является привилегированным –
в нем возможен доступ ко всем ресурсам процессора, так как процедура обслуживания
исключения «должна разобраться», что же произошло? И «найти» корректный выход из
73

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F
сложившейся ситуации. Он может быть либо очень простым – остановить ход выполнения
программы, либо более сложным. Так, при исключении «деление на ноль», если это
возможно с точки зрения конкретного приложения, можно продолжить выполнение
программы, выдав в качестве результата операции максимально возможное значение с
соответствующим знаком.
Для управления режимами работы процессора используется регистр управления –
Control Register. Далее на примерах мы покажем, как выполняется инициализация
процессора, как разрешить/запретить доступ к специальным регистрам и как обеспечить
возврат из процедуры обслуживания исключения в основную программу.
Запретить
модификацию
регистра управления
(Control Register)

Сброс

Режим пользователя
User Mode (Thread Mode)

Режим пользователя
User Mode (Thread Mode)

Привилегированный доступ
(Privilegied)

Непривилегированный доступ
(Unprivilegied)

Возврат из
процедуры
обработки
исключения
Исключение

Разрешить
модификацию
регистра управления
(Control Register)

Режим
обработчика исключения
Handler Mode

Возврат из
процедуры
обработки
исключения
Исключение

Привилегированный доступ
(Privilegied)

Рис. 5.2 Режимы работы процессоров Cortex-M
Процедура инициализации процессора очень важна. Только после ее
выполнения можно передать управление пользовательской программе. При
инициализации процессора выполняется доступ к специальным регистрам –
это можно сделать только в привилегированном режиме работы, который и
устанавливается «по умолчанию» при сбросе или включении питания.
Переход в непривилегированный режим работы выполняется исключительно
программным путем.
Переход в режим обработчика исключений Handler Mode выполнятся
автоматически, в момент детектирования процессором нестандартной ситуации и
обращения к программе – обработчику этого исключения.

5.5 Операционные состояния процессора
Процессор может находиться в двух операционных состояниях: либо выполнять
инструкции, либо находиться в состоянии останова (в точке останова при отладке
программы):
• Thumb state – состояние выполнения команды набора Thumb. Это нормальный
режим работы, когда выполняются 16- или 32-разрядные команды единого набора
команд Thumb, выровненные по границе слова или полуслова. Это общее
состояние, независимо от типа конкретной команды (32-разрядной ARM) или 16разрядной команды (Thumb-2). Напомним, что используемый в рассматриваемых
процессорах набор команд Thumb объединяет оба набора команд в один общий.
74

ГЛАВА 5. АРХИТЕКТУРА ПРОЦЕССОРНЫХ ЯДЕР ARM CORTEX-M3/4/4F


Debug State – состоянии отладки, когда процессор находится в состоянии останова
для выполнения любых отладочных операций. Управление всеми внутренними
ресурсами процессора при этом передается отладчику.

Список рекомендуемой литературы
1) Джозеф Ю. Ядро Cortex-M3 компании ARM. Полное руководство/ Ю. Джозеф; пер.
с англ. А.В. Евстафеева. – М.: Додэка-XXI, 2012. – 552 c. ил. (Мировая
электроника).
2) Козаченко В.Ф. Микроконтроллеры: руководство по применению 16-разрядных
микроконтроллеров Intel MCS-196/296 во встроенных системах управления. – М.:
ЭКОМ, 1997. – 688 с.
3) ARM DDI 0403C_errata_v3, ARMv7-M Architecture Reference Manual, Arm Limited,
2010.
4) Texas Instruments. SPMU 159a. Cortex-M3/M4F Instruction Set. Technical User’s
Manual. Texas Instruments Inc. – 2011.
5) ARM DDI 0337H, Cortex-M3. Technical Reference Manual. Arm Limited, 2010.
6) ARM DUI 0068B. ARM Developer Suite. Assembler Guide. Arm Limited, 2001.
7) ARM DUI 0553A. Cortex-M4 Devices. Generic User Guide. Arm Limited, 2010.
8) ARM DDI 0439D. ARM Cortex-M4 Processor. Technical Reference Manual. Arm
Limited, 2013.
9) ARM DDI 0479B. Cortex-M System Design Kit. Technical Reference Manual. Arm
Limited. – 2011.

75

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM

6 УНИФИЦИРОВАННАЯ КАРТА
ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР
ARM
Оглавление
6.1. Общие сведения .................................................................................................................... 76
6.2. Начальные сведения о проекте ........................................................................................... 77
6.3. Порядок расположения данных в памяти .......................................................................... 78
6.4. Объем прямо-адресуемой памяти в процессорах Cortex-M ............................................. 79
6.5. Адресация регистров периферийных устройств ............................................................... 80
6.6. Унифицированная карта памяти ......................................................................................... 80
6.7. Типы и атрибуты областей памяти ..................................................................................... 82
6.8. Особенности доступа к различным областям памяти ...................................................... 83
6.9. Размещение в памяти таблицы векторов прерываний и исключений............................. 84
6.9.1. Код инициализации указателя стека ........................................................................... 84
6.9.2. Начальный адрес процедуры инициализации процессора ....................................... 85
6.9.3. Начальный адрес обработчика немаскируемого прерывания .................................. 85
6.9.4. Аппаратные ошибки ..................................................................................................... 86
6.9.5. Программные ошибки .................................................................................................. 87
6.9.6. Программный запрос вызова супервизора ................................................................. 87
6.9.7. Отладочный монитор ................................................................................................... 88
6.9.8. Сервисная служба PENDSV ........................................................................................ 88
6.9.9. Прерывание от системного таймера ........................................................................... 89
6.9.10. Прерывания от встроенных периферийных устройств........................................... 89

6.1 Общие сведения
Карта памяти процессора определяет возможную структуру
памяти микроконтроллера, изготовленного на его основе, т.е. возможное
распределение памяти на области: встроенной кодовой памяти (памяти
программ); встроенной памяти для чтения и записи (оперативной памяти);
дополнительной внешней памяти; памяти периферийных устройств;
памяти специального назначения.
Под внутренней памятью понимается встроенная в кристалл микроконтроллера
память, а под внешней – дополнительно подключаемая к нему в случае необходимости
(обычно для расширения объема внутренней памяти в больших и сложных проектах).
Процессоры Cortex-M3/M4/M4F имеют унифицированную карту памяти,
задающую максимально возможные диапазоны адресов всех возможных областей памяти,
76

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM
типы (назначение) и особенности использования этих областей памяти (атрибуты).
Производители конкретных микроконтроллеров должны следовать этим общим
правилам, размещая реальную память только в предназначенных для этого областях.
Только в этом случае все внутренние интерфейсы процессора будут работать в
оптимальных режимах.
Фактическая карта памяти конкретного микроконтроллера используется при
разработке программного обеспечения. Она задается в виде допустимых диапазонов
адресов соответствующих областей памяти и позволяет программе компоновщику
(линкеру) размещать код программы и данные только в реально существующих областях
памяти программ и данных, контролируя возможные переполнения этих областей.
Фактическая карта памяти микроконтроллера будет содержать также список
реальных адресов регистров встроенных в микроконтроллер периферийных устройств.
Эта информация обязательно содержится в техническом описании каждого выпускаемого
микроконтроллера (в его Data Sheet).

6.2 Начальные сведения о проекте
Любой программный проект предназначен для решения конкретной прикладной
задачи и состоит из определенного числа программных модулей, написанных одним или
несколькими программистами. Каждый программный модуль представляет собой
программу на языке ассемблера или на языке высокого уровня, которая выполняет
определенные функции, обращаясь к переменным, расположенным в оперативной памяти
микропроцессорной системы. Для каждого из программных модулей могут
резервироваться свои собственные переменные в ОЗУ. Часть переменных может быть
общей для всех или нескольких программных модулей. Все переменные проекта делятся
на два типа:
• Инициализируемые, начальные значения которых перед запуском программы должны
быть определены (установлены);
• Неинициализируемые, начальные значения которых устанавливать не требуется. Об
этом позаботится программа пользователя.
В языке Ассемблер или в любом языке высокого уровня должны быть и имеются
средства не только для написания собственно программы модуля, но и для
резервирования в памяти переменных – инициализируемых и неинициализируемых.
После трансляции исходных файлов, написанных на ассемблере или компиляции
исходных файлов, написанных на языке высокого уровня, которая завершается созданием
машинного кода каждого модуля, все коды объединяются в один программный код
проекта компоновщиком (линкером) и размещаются в последовательной области кодовой
памяти микроконтроллера. Одновременно объединяются и области памяти
инициализированных и неинициализированных переменных в ОЗУ, как показано на рис. 6.1.
Основная задача компоновщика – выполнять это объединение и следить за тем, чтобы
имеющихся в МК ресурсов памяти было достаточно, в противном случае –
информировать разработчика о недостатке объема кодовой памяти или памяти данных.

77

Программный
модуль 2

Переменные модуля 2
Инициализированные
Неинициализированные

Программный
модуль n

Переменные модуля n
Инициализированные
Неинициализированные

Неинициализированные

Переменные модуля 1
Инициализированные
Неинициализированные

Инициализированные

Программный
модуль 1

Переменные проекта
(в ОЗУ МК)

Программа проекта
(в кодовой памяти МК)

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM

Рис. 6.1 Понятие проекта
Любой проект состоит из определенного числа программных
модулей, написанных на языке ассемблера или на языке высокого уровня.
После трансляции или компиляции каждого из модулей кодовые части
модулей объединяются в один код проекта для размещения в памяти
программ, а области инициализированных и неинициализированных
переменных – в общие области для размещения в ОЗУ.
Компилятор сначала преобразует программу, написанную на языке высокого
уровня, в программу на языке Ассемблер конкретного процессора, а затем запускает
процесс трансляции с языка Ассемблер в машинный код этого процессора.

6.3 Порядок расположения данных в памяти
Процессоры ARM имеют 32-разрядную адресную шину, по которой могут
адресовать в памяти данные (объекты) разной длины:
• 8-разрядные – байты;
• 16-разрядные – полуслова;
• 32-разрядные – слова.
С объектами большей длины, например, 64-разрядными двойными словами,
процессор работает последовательно, извлекая из памяти младшее, а затем старшее слово
двойного слова.
Процессоры ARM рассматривают память как линейную область 8-разрядных чисел
(байт), каждый из которых имеет свой персональный адрес, начиная с нулевого адреса до
0xFFFF FFFF. Два последовательно расположенных байта образуют 16-разрядное
полуслово (Halfword), а два последовательно расположенных полуслова – полное 32разрядное слово (Word). Адреса полуслов, как правило, выравниваются по четным
адресам (кратным 2), а адреса слов – по дважды четным адресам (кратным 4).
Каждое слово состоит из четырех байт, при этом используется так называемый
прямой порядок расположения байт и полуслов в слове: по младшему адресу в памяти
располагается младший байт и, соответственно, младшее полуслово.
Ниже показан порядок расположения байт в слове 0x55AAFE02, расположенном по
адресу 0x2000 0000. Для обозначения старшего полуслова используется мнемоника
MSHW (старшее значащее полуслово), а для обозначения младшего – LSHW (младшее
значащее полуслово). Аналогичные сокращения используются для обозначения старшего
значащего байта MSB и младшего значащего байта LSB (см. табл. 6.1).

78

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM
Таблица 6.1 Порядок расположения байт в слове
Адреса ячеек
памяти

Байты
Hex

Байты
Bin

Полуслова
Halfword

Слова
Word

0xFFFF FFFF

0x2000 0003
0x2000 0002
0x2000 0001
0x2000 0000

0x0000 0000

55
AA
FE
02

0101 0101
1010 1010
1111 1110
0000 0010

MSHW=0x55AA
LSHW=0xFE02

MSB
LSB
MSB
LSB

MSB
W=0x55AAFE02
LSB

Прямой порядок расположения бит используется также в байте, полуслове и слове,
т.е. вначале располагается самый младший бит, затем – более старшие биты.
Каждый байт содержит 8 бит двоичной информации. Биты нумеруются, начиная с
младшего бита: 0, 1, 2, … Таким образом, самый старший бит байта имеет номер 7,
полуслова 15, слова 31:
32-разрядне слово
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
16-разрядное старшее полуслово
15 14 13 12 11 10 9 8 7 6 5
Старший байт старшего
полуслова
7

6

5

4

3

2

1

0

4

3

2

1

0

16-разрядное младшее полуслово
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

Младший байт старшего Старший
полуслова
младшего
полуслова
7 6 5 4 3 2 1 0 7 6 5 4

байт Младший
байт
младшего полуслова
3

2

1 0 7 6 5 4 3 2 1 0

Рис. 6.2 Порядок расположения бит в слове
В процессорах ARM свой адрес имеет каждый байт, полуслово и
полное слово. Исключение составляют некоторые специальные области
памяти, в которых содержатся так называемые битовые ленты – области
памяти, которые адресуются как побайтово или пословно, так и побитово.
Работа с «битовыми лентами» рассматривается отдельно (глава 13).
Любые массивы и структуры данных адресуются по начальному адресу их
расположения в памяти.

6.4 Объем прямо-адресуемой памяти в процессорах Cortex
Он измеряется в единицах информации, которые могут быть записаны или
считаны из памяти. Такой минимальной единицей в данном случае является байт. Имея
32-разрядную шину адреса процессоры ARM могут адресовать 232= 4 Гбайт данных, 2 Г
полуслов или 1 Г слов. Напомним, что в процессорной технике принято обозначать
масштабирующие коэффициенты так:




K – Кило = 1024;
М – Мега = 1024 x 1024 = 1 048 576;
Г – Гига = 1024 x 1024 x 1024 = 1 073 741 824.

Имеющиеся объемы памяти перекрывают потребности большинства разработчиков
встраиваемых в оборудование цифровых систем управления. Однако, Вы должны иметь в
79

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM
виду, что распределение этого объема памяти по назначению жестко регламентируется
фирмой ARM. Так, разработчик конкретного микроконтроллера не может отдать весь
объем прямо-адресуемой памяти под кодовую память. Часть общего объема займет
оперативная память, внешняя память и периферийные устройства. Но даже такого объема,
который фирма ARM резервирует для кодовой памяти (0.5 Г байта), больше чем
достаточно для большинства приложений.

6.5 Адресация регистров периферийных устройств
В процессорах ARM применяется механизм адресации регистров периферийных
устройств (ПУ), называемый отображением адресов регистров ПУ на память. Часть
адресного пространства выделяется для периферийных устройств и называется областью
памяти периферии. При этом одна область памяти предназначена для размещения
встроенных на кристалл микроконтроллера периферийных устройств, а вторая – внешних,
подключаемых при необходимости уже разработчиком конкретной микропроцессорной
системы. Преимущества:
1) Для доступа к регистрам периферийных устройств можно использовать весь арсенал
команд процессора, предназначенный для работы с памятью. Никаких специальных
команд ввода/вывода нет.
2) Это касается и непосредственной работы с битами регистров периферийных устройств
(команд так называемого «битового сопроцессора», – можно устанавливать и
сбрасывать биты при инициализации или изменении режимов работы ПУ, считывать и
обрабатывать битовую информацию из портов ввода, выводить информацию в порты
вывода обычными командами работы с памятью (глава 13). Это, правда, возможно
только в том случае, если периферийные устройства, интегрированные на кристалл
разработчиком микроконтроллера, расположены в зоне адресов, допускающих
побитовую адресацию в соответствии с унифицированной картой памяти ARM.
3) Системные периферийные устройства (например, контроллер прерываний, таймер),
относящиеся к периферии, встроенной в ядро ARM-процессора, располагаются в своей
персональной области памяти. Их адреса не меняются при переходе разработчика на
микроконтроллер с большей производительностью или другим объемом встроенной
памяти, другим набором специализированной периферии. Это обеспечивает как
аппаратную, так и программную совместимость при модернизации и разработке новых
устройств. Без каких-либо изменений можно использовать программное обеспечение
верхнего уровня – операционные системы реального времени и мониторы,
использующие в своей работе исключительно системные периферийные устройства.

6.6 Унифицированная карта памяти
Ниже представлена унифицированная карта памяти процессоров CortexM3/M4/M4F. Разработчикам и производителям микроконтроллеров предписывается
размещать свою кодовую и оперативную память в строго определенных адресных
диапазонах, а также использовать в качестве адресов регистров периферийных устройств
специально выделенный для этой цели диапазон адресов памяти (см. табл. 6.2).

80

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM
Таблица 6.2 Унифицированная карта памяти процессоров Cortex-M
Диапазон
Область
Объем
адресов
памяти
0xFFFF FFFF Vendor specific 511 Мбайт
memory
Память,
определяемая
производите0xE010 0000 лем МК
0xE00F FFFF Private
1 Мбайт
Peripheral Bus

Тип

Атрибут

Назначение

Device

XN

Память, назначение которой
определяется исключительно
производителем
микроконтроллера (МК)

Strongly- XN
ordered

Собственная
периферийная
шина
0xE000 0000 процессора

Регистры периферийных
устройств центрального
процессора:
контроллера прерываний NVIC;
системного таймера – System
timer;
Блока управления системой Block
system control
Регистры внешних
периферийных устройств

0xDFFF FFFF External device

1 Гбайт

Device

XN

Внешние
0xA000 0000 устройства
0x9FFF FFFF External RAM

1 Гбайт

Normal

-

Данные исполняемой программы

0x6000 0000 Внешнее ОЗУ
0x5FFF FFFF Peripheral

0.5 Гбайта Device

XN

0.5 Гбайта Normal

-

Регистры встроенных в
микроконтроллер периферийных
устройств.
Содержит также область
побитово-, побайтово-адресуемых
регистров
Данные исполняемой программы.
Может содержать код программы.

Встроенная
0x4000 0000 периферия
0x3FFF FFFF SRAM
Встроенное
статическое
0x2000 0000 ОЗУ
0x1FFF FFFF Code
Память
программ
0x0000 0000 (кодовая)

0.5 Гбайта Normal

-

Содержит также побитово-,
побайтово-адресуемую память.
Исполняемый код программы.
Могут храниться данные
(константы и таблицы констант)

Понятие типа и атрибута области памяти поясняется ниже. Цветным фоном
выделены области памяти, наиболее часто используемые прикладными программистами.
Обратите внимание на то, что области встроенного статического ОЗУ (SRAM), а также
встроенных регистров периферийных устройств (Peripheral) содержат специальные
разделы памяти для поддержки операций с битами (битовые ленты). Если разработчики
конкретного микроконтроллера разместили свои периферийные устройства в другой
области, например, в области внешних периферийных устройств, то побитовый доступ к
ним станет невозможным.
1)
Проверьте, действительно ли общий объем памяти в соответствии с
унифицированной картой памяти равен 4 Г байта?
2)
Вспомните, какие из периферийных устройств являются
встроенными в центральный процессор?
3)
С какой целью все периферийные устройства разделены на две

81

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM
группы: принадлежащие собственно процессору; принадлежащие микроконтроллеру?
4) В какую область памяти попадают модули поддержки отладки?
1)
Да, это так.
2)
Прежде всего контроллер прерываний и системный таймер, а
также модули, поддерживающие интерактивную отладку.
3)
С целью обеспечения предельного быстродействия встроенных в
процессор периферийных устройств, которые используются всеми
разработчиками.
4) В область устройств на собственной периферийной шине процессора.

6.7 Типы и атрибуты областей памяти
Каждая область памяти предназначена для размещения в ней данных
определенного типа. Она может иметь дополнительные атрибуты, связанные со
спецификой доступа к данным в этой области, что и отмечено в соответствующих
позициях карты памяти.
Информация о типах и атрибутах областей памяти (табл. 6.3, 6.4)
предназначена, главным образом, для разработчиков микроконтроллеров.
Конечные пользователи могут только проверить, что используемые ими
микроконтроллеры соответствуют требованиям фирмы ARM.
Таблица 6.3 Типы области памяти
Тип области памяти

Особенности

Normal – Нормальная

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

Device – Устройство

Strongly-ordered
Строго по очереди

В процессоре имеется специальная система автоматического упорядочивания
доступа к памяти, которая оптимизирует этот процесс. Поэтому, фактический порядок
доступа может отличаться от предусмотренного программой пользователя, если это не
влияет на конечный результат. Если результат выполнения программы жестко зависит от
последовательности доступа к памяти (например, при инициализации периферийных
устройств или самого процессора), то программист должен разделить инструкции
обращения к памяти специальной командой разделения доступа memory barrier.
Тип памяти «Строго по очереди» имеют только системные периферийные
устройства. Если при последовательном обращении к двум областям памяти:
• Одна из областей имеет тип «Нормальная», то программный порядок обращения не
гарантируется. Процессор может выполнять автоматическую оптимизацию доступа
(изменить порядок, определенный программой) с целью повышения
производительности всей системы.
• Обе области памяти имеют тип «Строго по очереди», то команды выполняются в
порядке их расположения в программе. При работе со встроенной периферией
процессора это дает уверенность, что все действия будут выполнены строго в
соответствии с желанием программиста.

82

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM
Дополнительные атрибуты областей памяти описаны в табл. 6.4.
Таблица 6.4 Дополнительные атрибутам областей памяти
Атрибут

Что означает

Shareable – Совместное
использование

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

Execute Never (XN) –
Никогда не выполнять

Атрибут «Совместного использования» имеют некоторые подобласти памяти
периферийных устройств (не указаны в приведенной выше карте памяти). Он имеет
значение только для мульти-микропроцессорных систем, которые в этой книге не
рассматриваются.
Атрибут «Никогда не выполнять» имеет большое значение. Если в результате
неправильного программирования или программного сбоя последует считывание
программного кода, например, из области памяти периферийных устройств, то будет
сгенерировано исключение fault (ошибка). Такая мера защиты существенно повышает
надежность систем встроенного управления оборудованием.
Обратите внимание на то, что все области памяти периферийных
устройств, встроенных в процессор, встроенных в микроконтроллер и даже
внешних ПУ, автоматически защищены от попытки считывания из них
программного кода. В этом случае будет генерироваться исключение по
ошибке fault.

6.8 Особенности доступа к различным областям памяти
Каждая область памяти на карте памяти процессора имеет
определенные особенности доступа к расположенной там информации:
1)
Код программы пользователя может размещаться в трех
областях: Code, SRAM, и external RAM. Однако, фирма ARM рекомендует
размещать код программы всегда в области кода Code. Теоретически код
программы может располагаться и во внутреннем ОЗУ – SRAM и во
внешнем – external RAM, что удобно при отладке. Однако, выполнение программы из
ОЗУ замедляется. Дело в том, что для доступа к программной памяти в области Code
используются две отдельные шины – для считывания кодов команд на конвейер
команд и для считывания данных. Гарвардская архитектура центрального процессора
обеспечивает одновременный и независимый друг от друга доступ к кодам команд (из
секции Code) и к данным как из других секций, так и из секции Code, что существенно
повышает производительность процессора.
2) В состав центрального процессора может включаться дополнительный модуль защиты
памяти Memory Protection Unit (MPU), который предназначен для изменения типов и
атрибутов областей памяти, указанных на карте памяти и являющихся параметрами
областей памяти по умолчанию.
3) С помощью этого блока могут устанавливаться также дополнительные ограничения на
доступ к памяти для совместного использования (shared memory) в
мультипроцессорных системах. При этом сама область памяти может разделяться на
подобласти со своими индивидуальными типами и атрибутами доступа.

83

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM

6.9 Размещение в памяти таблицы векторов прерываний и
исключений
В процессорах Cortex-M контроллер прерываний вместе с детектором
исключительных ситуаций интегрированы на кристалл процессора. Обработка всех
прерываний и исключений выполняется соответствующими подпрограммами,
обработчиками прерываний/исключений, написание которых – задача программиста.
Начальные адреса обработчиков должны быть записаны программистом в таблицу
векторов прерываний и исключений, с которой далее в автоматическом режиме будет
работать контроллер прерываний. При возникновении прерывания или исключения (если
оно разрешено) контроллер прерываний считывает из таблицы начальный адрес
соответствующего обработчика и загружает его в счетчик команд PC, передавая
управление обработчику прерывания/исключения.
Сразу после включения питания или сброса процессора предполагается, что
таблица векторов прерываний и исключений находится в самом начале памяти
программ, начиная с нулевого адреса. При необходимости ее положение в памяти системы
можно изменить. Для этой цели предназначен один из регистров встроенного контроллера
приоритетных прерываний NVIC – VTOR. Его значение после сброса процессора – ноль.
В табл. 6.5 приведена структура таблицы векторов прерываний и исключений
процессоров Cortex-M. Цветом выделены различные функциональные группы запросов
прерываний.
Таблица 6.5 Таблица векторов прерываний и исключений (сразу после сброса ЦПУ)
Номер
искл.-я/
прер.-я

Адресное
смещение

Обозначение
запроса

Содержит начальный адрес обработчика
запроса прерывания
/исключения

18…255
17
16
15
14
13
12
11
7…10
6
5
4
3
2
1
0

0x48…0x3FF
0x44
0x40
0x3C
0x38
0x34
0x30
0x2C
0x1C…0x28
0x18
0x14
0x10
0x0C
0x08
0x04
0x00

IRQ 2…239
IRQ 1
IRQ 0
SYSTICK
PendSV
Зарезервировано
Debug Monitor
SVCall
Зарезервировано
Usage Fault
Bus Fault
Mem Manage Fault
Hard Fault
NMI
Reset
MSP

От периферийного устройства, встроенного в
микроконтроллер
От системного таймера
Адрес супервизора или ОС РВ
Адрес отладочного монитора
Адрес супервизора или ОС РВ
Обработчика ошибки использования
Обработчика ошибки шины
Обработчика ошибки управления пам.-ю
Обработчика аппаратной ошибки
Обработчика немаскируемого прерывания
Обработчика сброса процессора
Начальное значение указателя стека MSP

6.9.1 Код инициализации указателя стека
Первый вектор в таблице – особый. Это не начальный адрес какого-либо
обработчика, а значение указателя стека микропроцессорной системы SP. По сути – это
код инициализации вершины стека в системе – адреса последней «как бы занятой
данными» ячейки памяти, отведенной пользователем под стек. Мы подробно рассмотрим
назначение стека и особенности работы с ним в следующей главе. Пока же отметим, что
стек – это часть памяти данных, специально отведенная программистом для временного

84

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM
хранения информации, в том числе адресов возврата при вызове подпрограмм и
обработчиков прерываний/исключений. При записи данных в стек содержимое указателя
стека SP автоматически декрементируется (на 4), так как в процессорах Cortex-M
используется так называемый «автодекрементный» стек (см. 7.7.3). После этого нужное
слово (4 байта) сохраняется в памяти.
Итак, по начальному адресу в кодовой памяти программист должен записать код
инициализации указателя стека. Сразу после сброса процессор автоматически считает
этот код и загрузит его в указатель стека SP.
Зачем это делается? Если уже в самом начале работы программы пользователя
возникнет какая-то непредвиденная ситуация, например, аппаратная ошибка, связанная с
аппаратной ошибкой при проектировании системы, то процессор должен вызвать
соответствующий обработчик ошибок. Но, прежде нужно записать адрес возврата в стек.
А если он не определен? Именно для повышения надежности программного обеспечения
инициализация стека выполняется автоматически в самом начале работы процессора,
причем еще до инициализации самого процессора и памяти данных (если это требуется).
Стек располагается в ОЗУ. В соответствии с унифицированной картой памяти
процессоров ARM Cortex-M область ОЗУ: 0x20000000-0x3FFFFFFF (512 М байт). Зная
реальный объем ОЗУ Вашей системы, Вы можете в конце этой области памяти разместить
стек. Так, если объем ОЗУ 256 Мбайт, то указатель стека можно проинициализировать
числом 0x30000000 (первая запись будет в ячейку памяти 0x2FFFFFFС).

6.9.2 Начальный адрес процедуры инициализации процессора
Второй вектор тоже особый. Это начальный адрес процедуры инициализации
центрального процессора и, в общем случае, содержимого оперативной памяти системы
(установки значений инициализируемых переменных). Фактически это адрес одной из
важнейших программ любого проекта – стартовой программы Startup. Ее задача –
выполнить все необходимые начальные установки в системе и затем передать управление
программе пользователя (более подробно – в разделе 9.1). Каждый раз, когда Вы будете
включать питание или нажимать на кнопку «Сброс», будет выполняться
переинициализация системы.
Стартовая программа Startup должна обязательно присутствовать в
любом проекте. Именно она будет содержать код начальной инициализации
процессора. Более того, в ней будут инициализироваться нужные
пользователю периферийные устройства (как встроенные в процессор, так и
в микроконтроллер), даваться разрешение на работу сопроцессора (если это
необходимо), а также инициализироваться области памяти данных, предназначенные для
переменных.
Обратите особое внимание на то, что при включении питания состояние ячеек
памяти данных не определено – там может быть произвольная информация.
Стартовая программа обычно пишется на языке Ассемблер, даже если разработка
программного обеспечения выполняется на языке высокого уровня С/С++. В ее конце
выполняется безусловная передача управления на начало программы пользователя,
написанной на ассемблере, или на начало функции main на СИ. При использовании СИ
функция main может содержать процедуры начальной инициализации нужных
конкретному пользователю периферийных устройств.

6.9.3 Начальный адрес обработчика немаскируемого прерывания
Третий вектор – это начальный адрес процедуры обслуживания так называемого
немаскируемого прерывания Non-Maskable Interrupt (NMI). Все прерывания (внешние и от
встроенных в микроконтроллер периферийных устройств) делятся на два класса:
85

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM
маскируемые и немаскируемые. Первые могут быть запрещены – замаскированы, путем
записи соответствующей информации в регистры контроллера прерываний, а вторые нет,
– их обработку нельзя «отключить». К числу немаскируемых прерываний относятся
прерывания от аварийных датчиков, реакция на срабатывание которых обеспечивает
«живучесть» объекта управления.
Это могут быть датчики аварийного состояния и самой микропроцессорной
системы управления, например, отказа памяти или какого-либо важного периферийного
устройства. Если датчик такой аварии подает сигнал на вход NMI контроллера
прерываний, то в процедуре обслуживания прерывания выполняется немедленное
отключение оборудования во избежание дальнейшего развития аварии.
На вход немаскируемого запроса прерывания микроконтроллера
обычно подается сигнал с датчиков, сигнализирующих о невозможности
дальнейшей работы оборудования в штатном режиме. В процедуре
обслуживания запроса делается все возможное для предотвращения аварии.
Места в таблице векторов прерываний с 3 по 14 (12 позиций) отведены
под начальные адреса процедур обработки исключений. Обработка
исключений – это специальная тема, которая должна изучаться только после
изучения системы команд процессора и общих приемов программирования.
Она сложна не только для начинающих, но и для квалифицированных программистов.
Поэтому поступим так: дадим здесь лишь краткий обзор возможных источников
исключений и прерываний в процессорах Cortex-M, чтобы завершить описание таблицы
векторов. Это позволит нам понять структуру стартового программного модуля Startup,
предлагаемого фирмой ARM в качестве шаблона для использования прикладными
программистами. Приведенная ниже информация при первом чтении может быть бегло
просмотрена.

6.9.4 Аппаратные ошибки
В начале таблицы располагаются адреса обработчиков ошибок при выполнении
команд, когда процессор не может корректно завершить выполнение операции.
Возникновение этих неисправностей контролируется на аппаратном уровне ARM-ядра
процессора соответствующими детекторами. Приведем кратное описание причин
возникновения этих ошибок:
• Hard fault – Аппаратная ошибка в схемотехнике микроконтроллера (МК) или
микропроцессорной системы (МПС).
• Memory management fault – Ошибка управления памятью. Может возникнуть
только тогда, когда в состав ЦПУ включен опциональный блок защиты памяти
MPU (Memory Protection Unit), который в состоянии идентифицировать
некорректный доступ к памяти.
• Bus fault – Ошибка шины. Может проявиться на стадии разработки МК или МПС:
при невозможности считывания кода команды на конвейер команд; при
невозможности доступа к данным по записи/чтению; при некорректном доступе к
периферии (например, при обращении к байтовому регистру периферийного
устройства командой чтения слова).
Первые три ошибки сигнализируют о некорректности или
неисправности в аппаратной части МК или МПС. Их обработчики
используются, прежде всего, инженерами-аппаратчиками на стадии
тестирования новых микроконтроллеров на базе ядер ARM Cortex-M и
микропроцессорных систем на их основе. Если Вы покупаете
микроконтроллер известного производителя, то вероятность возникновения таких ошибок
86

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM
минимальна. Они могут быть следствием
микроконтроллеру
внешних
дополнительных
некорректного расширения памяти.

некорректного
периферийных

подключения к
устройств
или

6.9.5 Программные ошибки
Usage fault – Ошибка использования. Это программная ошибка, связанная с
некорректными действиями программиста. Именно она позволяет обнаружить такие
ошибки, как деление на ноль, извлечение квадратного корня из отрицательного числа и
т.д.
Ошибка использования не имеет отношения к аппаратной части. Причина ее
возникновения – неправильный алгоритм или неправильная его реализация
программистом. Поэтому очень важно, чтобы обработчик этой ошибки уже существовал
на стадии отладки программного обеспечения. Его важнейшая задача – сигнализировать
программисту о некорректности работы программы. В простейшем случае это останов
программы и светодиодная индикация причины возникновения ошибки на плате МПС. В
более сложном случае – попытка устранения ошибки и продолжение выполнения
программы.
Так, деление на ноль чисел с фиксированной точкой – это фатальная ошибка.
Можно сообщить о ней программисту и «заставить» его модифицировать программу. При
делении на ноль чисел с плавающей точкой – ситуация иная. Обработчик ошибки может
не прерывать выполнение программы, а вернуть в качестве результата операции
положительную или отрицательную бесконечность. Вычислительный процесс или
процесс управления может быть продолжен.
Одна из наиболее частых причин возникновения ошибки
использования – считывание из кодовой памяти неопределенной инструкции
(undefined instruction). Это возможно, если код, поступивший на конвейер
команд, не может быть декодирован.
Ошибка использования – это ошибка программиста. Самое простое –
обработчик ошибки должен сигнализировать о ее появлении программисту. Он должен
присутствовать в составе пользовательского ПО уже на стадии отладки.

6.9.6 Программный запрос вызова супервизора
Одна из позиций в таблице векторов прерываний/исключений может содержать
начальный адрес специальной программы, называемой супервизором или монитором. Это
может быть и операционная система реального времени (ОС РВ), которая куплена
разработчиком конкретных приложений, в том числе, с целью использования типовых,
оптимизированных функций ввода/вывода данных по стандартным интерфейсам.
Чтобы из пользовательской программы (нижнего уровня) обратиться к супервизору
(программе верхнего уровня) в системе команд Cortex-M предусмотрена специальная
команда так называемого программного вызова супервизора SVC #imm (SerVice Call). Это
не аппаратное прерывание, а программное, сгенерированное специальной командой в
пользовательской программе. При ее выполнении автоматически считывается адрес в
позиции 11 таблицы векторов прерываний, загружается в счетчик команд и управление
передается программе-супервизору.
В команде SCV задается также 8-разрядный непосредственный операнд #imm,
который определяет номер услуги (0÷255), которую хочет получить программист нижнего
уровня от программы-супервизора. Он идентифицируется супервизором, после чего
вызывается нужная функция. Список имеющихся функций определяется разработчиком
супервизора.

87

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM
В чем преимущества такого подхода? В том, что приложение пользователя может
«не задумываться» о том, как фактически выполняется нужная функция и какие при этом
используются ресурсы. Оно не обращается к конфигурационным регистрам процессора и
регистрам периферийных устройств. Четкое разделение между службами разного уровня
– гарантия надежной работы программного обеспечения, когда программы нижнего
уровня не допускаются к системным ресурсам во избежание случайного
малоквалифицированного доступа.

6.9.7 Отладочный монитор
Ядро процессоров Cortex-M3/М4/М4F имеет встроенную систему аппаратной
поддержки разработки и отладки программного обеспечения, называемую системой
отладки (debug unit). Эта система позволяет подключать внешние компьютерные системы
отладки (например, по интерфейсу JTAG) и не требует дополнительной программной
поддержки. Все нужные для отладки средства уже реализованы на кристалле процессора.
Система отладки позволяет полностью взять на себя управление кодом программы, втом
числе исполнять его пошагового, от одной точки останова до другой с контролем
состояния регистров и памяти. Выход из режима отладки возможен только по сбросу
процессора Reset или при формировании запроса немаскируемого прерывания NMI.
Недостаток простых систем отладки – не обеспечивается режим отладки в
реальном времени. Например, в системах управления приводами необходимо, чтобы в
процессе отладки работали все или часть периферийных устройств, в частности,
многоканальный генератор периодических сигналов. Более того, необходимо, чтобы
функционировала система прерываний и запросы прерываний от генератора
обслуживались. Только при этом параметры управления генератором будут постоянно
обновляться (период или скважность выходных ШИМ-сигналов) и программист сможет
наблюдать за изменением управляющих сигналов и реакцией на них объекта управления в
«реальном времени».
Отладку в реальном времени в процессорах Cortex-M могут поддерживать
специальные
программы,
написанные
самостоятельно
или
купленные
у
специализированных фирм – отладочные мониторы Debug Monitor. Начальный адрес
размещения таких программ соответствует 12-му исключению в таблице векторов
прерываний/исключений. Любые события, требующие отладки, не будут полностью
останавливать процессор и периферию (состояние «Halt»), а будут вызывать переход к
программе монитора по запросу прерывания trap.
Контроллер прерываний позволяет разрешить или запретить использование такого
отладочного монитора. Запуск или останов монитора выполняется с помощью
конфигурационных регистров отладочных модулей ядра процессора. Написание
программы «Монитор» по силам только очень квалифицированному коллективу
разработчиков. Мы в этой книге будем пользоваться исключительно стандартными
средствами отладки, поддерживаемыми ядром процессора.

6.9.8 Сервисная служба PENDSV
Это может быть служба верхнего уровня (супервизор, монитор, ОС РВ) – та же или
аналогичная службе SVCall. Отличие состоит в том, что она вызывается не командой
программного прерывания SVC #imm, а установкой специального бита ждущего
прерывания в одном из регистров контроллера прерываний.
Вторая возможность вызова супервизора связана с тем, что программное
прерывание не всегда может быть активизировано. Это нельзя сделать, в частности, из
программы-обработчика прерывания/исключения с более высоким приоритетом. Самыми
высокоприоритетными являются запросы, расположенные в нижней части таблицы
88

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM
векторов (Reset, NMI). Так что, если обслуживается прерывание NMI, то программный
вызов сервиса невозможен. Такая попытка вызовет аппаратный отказ (hard fault).
О назначении сервисной службы мы уже упоминали – это возможность
приложения низкого уровня (пользовательского) обратиться к процедурам программы
высокого уровня, в частности, для передачи/приема данных по одному из интерфейсов.

6.9.9 Прерывание от системного таймера
Позицию 15 в таблице векторов прерываний/исключений занимает начальный
адрес обработчика запроса прерывания от системного таймера SisTick. Системный таймер
предназначен для точного отсчета временных интервалов – организации так называемых
«Системных часов» процессора. Именно это устройство позволяет реализовывать
многозадачные операционные системы и ядра реального времени (real-time kernel). В них
используется специальный механизм распределения процессорного времени между
отдельными задачами. Приложение пользователя также может использовать системный
таймер для аппаратного отсчета нужных временных интервалов. Идея такова: таймер
генерирует запрос прерывания, например, каждую 1 мс. В процедуре обслуживания
запроса прерывания от таймера контролируется любое число временных интервалов,
заданных пользователем в количестве миллисекунд. С каждым «тиком» системных часов
заданная ненулевая уставка времени уменьшается на 1. Как только она становится равной
0, выдается сигнал об истечении соответствующей выдержки времени. Число таких
одновременно работающих программно-аппаратных таймеров не ограничено.

6.9.10 Прерывания от встроенных в микроконтроллер периферийных
устройств
Начиная с позиции 16 в таблице векторов прерываний/исключений должны
находиться начальные адреса обработчиков запросов прерываний от встроенных в
микроконтроллер его производителем периферийных устройств IRQ № 0…239. Всего
предусмотрено 240 запросов прерываний от периферийных устройств. Конкретное
назначение каждого запроса прерывания IRQ определяется фирмой – производителем
микроконтроллера. Оно зависит и от состава встроенной в микроконтроллер периферии, и
от ее сложности. Одно периферийное устройство может генерировать несколько разных
запросов прерываний.
В начале кодовой памяти должна располагаться таблица векторов
прерываний/исключений. Она обязательно должна содержать первые два
вектора для авто-инициализации указателя стека и передачи управления
программе Startup начальной инициализации микропроцессорной системы
(и процессора и памяти). Крайне желательно, чтобы в составе прикладного
ПО был и обработчик программных ошибок. Это позволит своевременно
идентифицировать некачественные алгоритмы и программы.
1)
Какие два обязательных программных модуля входят в
любой проект?
2)
Какие переменные называются неинициализированными?
3)
Инициализированными?

89

ГЛАВА 6. УНИФИЦИРОВАННАЯ КАРТА ПАМЯТИ ПРОЦЕССОРНЫХ ЯДЕР ARM
1)
Стартовый программный модуль Startup с инициализацией
процессора и собственно программный модуль пользователя
(пользовательская программа) Main.
2)
Те переменные, для которых в памяти данных просто
резервируется место (закрепляется за ними). Начальное состояние этих
областей памяти после сброса процессора или включения питания не
определено.
3) Такие переменные, начальные значения которых должны быть
установлены до выполнения пользовательской программы. Это происходит так:
компоновщик объединяет все инициализированные секции в памяти данных в одну
и располагает последовательность кодов инициализации переменных в
определенном месте кодовой памяти (делает копию инициализированной секции
данных в программной памяти); задает начальный адрес расположения
инициализированной секции данных в ОЗУ. Далее вступает в действие программа
Startup начальной инициализации процессора и памяти. Она копирует массив кодов
инициализации из ПЗУ в ОЗУ. Тем самым переменные в ОЗУ становятся
инициализированными.
При программировании только на Ассемблере обычно резервируют
место под переменные в ОЗУ, а их инициализацию выполняют в
пользовательской программе по мере необходимости. Это существенно
упрощает структуру стартовой программы Startup.

Список рекомендуемой литературы
1) Джозеф Ю. Ядро Cortex-M3 компании ARM. Полное руководство/ Ю. Джозеф;
пер.с англ. А.В. Евстафеева. – М.: Додэка-XXI, 2012. – 552 c. ил. (Мировая
электроника).
2) ARM DDI 0403C_errata_v3, ARMv7-M Architecture Reference Manual, Arm Limited,
2010.
3) Texas Instruments. SPMU 159a. Cortex-M3/M4F Instruction Set. Technical User’s
Manual. Texas Instruments Inc. – 2011.
4) ARM DDI 0479B. Cortex-M System Design Kit. Technical Reference Manual. Arm
Limited. – 2011.

90

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M

7 ПРОГРАММНАЯ МОДЕЛЬ
ПРОЦЕССОРОВ CORTEX-M
Оглавление
7.1. Структура программы на языке Ассемблер. Формат строки ........................................... 92
7.2. Программная модель процессора Cortex-M ....................................................................... 95
7.3. Регистровое окружение центрального процессора ........................................................... 95
7.4. Регистровое окружение сопроцессора ............................................................................... 97
7.5. Регистр управления Control ................................................................................................. 98
7.6. Влияние RISC-архитектуры процессора на его программную модель ........................... 98
7.7. Назначение и особенности использования регистров окружения ЦПУ ......................... 99
7.7.1. Регистры общего назначения ......................................................................................99
7.7.2. Счетчик команд PC.....................................................................................................100
7.7.3. Регистр-указатель стека. Понятие стека...................................................................102
7.7.4. Регистр связи ...............................................................................................................104
7.8. Регистр статуса программы ............................................................................................... 106
7.8.1. Регистр статуса программы и его компоненты .......................................................106
7.8.2. Флаги результатов операций в ЦПУ ........................................................................107
7.8.3. Программное управление формированием флагов результатов операций ..........108
7.9. Условная передача управления и условное выполнение команд .................................. 109
7.9.1. Коды условной передачи управления и условного выполнения команд ..............109
7.9.2. Блоки условного выполнения команд ......................................................................111
7.9.3. Примеры условной передачи управления ................................................................112
7.10. Унифицированный синтаксис команд процессора и сопроцессора ............................ 115
7.10.1. Синтаксис трех-операндных команд ......................................................................117
7.10.2. Синтаксис двух-операндных команд ......................................................................118
7.10.3. Синтаксис одно-операндных команд .....................................................................120
7.11. Гибкий второй операнд-источник. Попутные операции в сдвиговом регистре ........ 120
7.11.1. Гибкий второй операнд ............................................................................................120
7.11. 2. Второй операнд ― константа .................................................................................121
7.11.3. Аппаратная поддержка операций предварительной обработки данных в
сдвиговом регистре ...................................................................................................................123
7.11.4. Типы попутных операций ........................................................................................123

91

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
Под программной моделью процессора понимается технология
доступа к данным, которые могут рассматриваться в качестве операндов в
командах процессора, входящих в его систему команд (набор команд).
Речь идет прежде всего о регистрах, расположенных в сверхоперативной
памяти процессора, особенностях их использования в качестве операндов
команд, а также в качестве регистров-указателей для доступа к данным во
внешней по отношению к процессору кодовой памяти и памяти данных. В программную
модель процессора включаются также регистры специального назначения (управления
режимами работы процессора, сопроцессора, статуса программы), к отдельным битовым
полям которых или отдельным битам программисту приходится обращаться при
написании и отладке любой программы.
При создании системы команд любого процессора разработчики руководствуются
некоторыми утвержденными ими общими принципами, которые реализуются и в
технологии кодирования операций – присвоении командам определенных символических
имен – мнемокодов и в синтаксисе ассемблерных команд, то есть в способах возможного
формирования поля мнемокода и поля операндов команды. Знание этих принципов,
неразрывно связанных с архитектурой процессора, позволяет быстро и эффективно
изучить систему команд процессора и начать применять ее на практике.

7.1 Структура программы на языке Ассемблер. Формат
строки
Ассемблер – это язык программирования самого низкого уровня, в котором каждая
строка программы может содержать мнемокод одной, конкретной команды процессора.
Совокупность таких строк и является программой, написанной на Ассемблере.
Технология создания мнемокодов команд разрабатывается очень тщательно, чтобы, с
одной стороны, быть лаконичной (по возможности краткой), а с другой – отражать смысл
и последовательность действий, выполняемых командой. Все строки в Ассемблерной
программе имеют один и тот же унифицированный формат (см. табл. 7.1):
Таблица 7.1 Унифицированный формат команды

()

Разделитель

Обязательно
с первой
позиции
строки

Любое
число
пробелов
или
символ
табуляции
Tab

Мнемокод
Раздеоперации
или литель
директивы
Ассемблера
(псевдо-команды)
Пробел

Поле операндов Разде- Поле комментария
команды
или литель
поле операндов
директивы
Ассемблера
;

Начиная с первого символа строки может располагаться метка (Label) –
символическое имя, созданное программистом, обязательно начинающееся с буквы, в
котором могут использоваться как буквы, так и цифры, причем, как в верхнем, так и в
нижнем регистрах. В именах меток допускается использование и некоторых специальных
символов, в частности, символа нижнего подчеркивания «_», который обычно
применяется в качестве разделителя в составных именах для их большей
«читабельности».

92

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
Метка – это символическое имя, значение которого на стадии трансляции
(ассемблирования программы) соответствует текущему адресу расположения в памяти
кода команды или данных, помеченных этой меткой. Метками помечаются начальные
адреса подпрограмм или фрагментов основной программы, к которым планируются
обращения в программе (безусловные или условные переходы), а также адреса
расположения в памяти отдельных констант или таблиц констант. Метка является
опциональной – ставится в случае необходимости.
Для размещения в памяти констант, выделения места в памяти под переменные,
управления процессом ассемблирования и многих других целей применяются не команды
процессора, а так называемые директивы языка Ассемблер – псевдокоманды. Они не
транслируются в машинные коды, а являются «указаниями» для транслятора выполнить
те или иные действия, например, разместить по текущему адресу заданную
программистом константу. Ассемблер процессоров ARM имеет большое число директив,
с назначением которых и особенностями их использования мы будем постепенно
знакомить читателя.
Сразу после метки, через разделитель (любое число пробелов или
символ табуляции), в ассемблерной строке размещается мнемокод команды
процессора или мнемокод директивы Ассемблера.
Обратите особое внимание на то, что мнемокод команды не может
начинаться с первого символа строки – ему обязательно должен предшествовать, по
крайней мере, один пробел.
После мнемокода команды или псевдокоманды, через разделитель в виде, по
крайней мере, одного пробела, следует поле операндов команды или директивы
Ассемблера. Имена операндов отделяются друг от друга запятой. Число операндов
зависит от конкретной команды/директивы.
Ассемблерная строка может завершаться комментарием (поясняющим текстовым
сообщением), который следует после разделителя в виде точки с запятой «;».
При программировании на Ассемблере используются следующие соглашения «по
умолчанию»:
1) Метка может отсутствовать, но мнемокоду команды или директивы должен
предшествовать, по крайней мере, один пробел. Правилом хорошего тона считается
размещать все мнемокоды команд и директивы после символа Tab. Это позволяет
четко отделять метки от команд и хорошо структурировать программу.
2) В качестве меток не допускается использование так называемых «зарезервированных»
имен – мнемокодов команд или директив Ассемблера, имен регистров.
3) Все метки в программе должны иметь разные имена.
4) В программе допускаются пустые строки, которые не содержат ни кода, ни
комментария. Они являются своеобразными разделителями фрагментов программы,
повышая ее «читабельность».
5) Комментарий может начинаться с первой позиции строки сразу после символа «;». Это
удобно, если он содержит заголовок расположенного ниже фрагмента программы или
подпрограммы (функции). В таком заголовке обычно присутствует краткое описание
функции, способ передачи ей параметров и возврата результата, назначение
используемых в подпрограмме регистров.
6) Комментарий должен отражать основную суть алгоритма, заложенного в программу.
Он необходим для последующего сопровождения и модификации программы, если это
потребуется.

93

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
Здесь и далее в книге примеры программного кода будут набраны моноширинным
шрифтом:
ADD r1, #1
MOV r2, #2
M5 SUB r0, r2, r1

B M5
END

;
;
;
;
;
;
;

Увеличить содержимое регистра r1 на 1
Инициализировать регистр r2 числом 2
Вычесть из содержимого регистра r2
содержимое регистра r1, сохранить
разность в регистре r0
Повторить фрагмент
Перейти на метку M5

Первая команда примера ADD (от англ. Addition – сложение) выполняет
инкрементирование текущего содержимого регистра r1, причем величина приращения #1
задается в коде самой команды – используется так называемая непосредственная
адресация операнда (ее визитной карточной является символ «#», предшествующий
самому числу). Значение непосредственного операнда автоматически размещается в
формате команды транслятором с Ассемблера. Оно считывается из регистра команд ЦПУ
на стадии выполнения команды и используется в качестве второго операнда АЛУ (первый
извлекается из регистра общего назначения r1).
Непосредственная адресация операндов применяется в основном для
инициализации начальных значений в регистрах процессора, как это показано во второй
команде примера MOV (от англ. Move – перемещение).
Команда SUB (от англ. Subtraction – вычитание) вычитает из содержимого регистра
r2 содержимое регистра r1 и сохраняет разность в регистре r0.
Следующая строка содержит только комментарий. Он может просто рязделять
разные фрагменты программы, повышая ее «структурированность».
Команда B M5 (от англ. Branch – ответвление, переход) прерывает ход
последовательного выполнения программы и передает управление на метку M5.
В последней строке программы находится не команда процессора, а директива
Ассемблера END. Она указывает транслятору, что дальше ассемблерного текста нет и
процесс трансляции в машинные коды нужно прекратить. Эта директива должна быть
последней в любом программном модуле, написанном на Ассемблере и расположенном в
отдельном файле.
Имеются различные версии языка Ассемблер, разработанные
разными компаниями.
Все они обеспечивают 100-процентную
совместимость языком мнемокодов команд собственно процессора. Однако,
по составу директив (псевдокоманд) и их синтаксису версии могут
существенно отличаться. Так, в некоторых версиях Ассемблера после имени
метки обязательно должно следовать двоеточие, а директива END может отсутствовать.
Тем не менее, все Ассемблеры очень похожи друг на друга, в том числе, по составу
директив и выполняемым ими функциям.
В этой книге мы пользуемся языком Ассемблера, разработанным фирмой ARM –
ARM ASM. Именно он поддерживается в интегрированной среде разработки Keil μVision
EDI, используемой в книге.

94

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M

7.2 Программная модель процессора Cortex-M
Представим программную модель процессора в виде имеющихся в его
распоряжении операционных блоков, обеспечивающих выполнение команд, и
регистрового окружения этих блоков, содержащих регистры, которые могут
использоваться в командах процессора в качестве регистров-источников Rs и регистровприемников Rd данных, то есть в качестве операндов команд – рис. 7.1.
На рис. 7.1 изображена программная модель процессоров Cortex-M4F, имеющих на
кристалле встроенный модуль поддержки вычислений с плавающей точкой – FPU
(сопроцессор). Она справедлива и для младших моделей Cortex-M3/M4, но с учетом
отсутствия в них сопроцессора и его «регистрового окружения».

7.3 Регистровое окружение центрального процессора
В состав центрального процессора (ЦПУ) входят три независимых устройства
обработки данных (операционный блок процессора):
1) Арифметико-логическое устройство (АЛУ);
2) Аппаратный умножитель;
3) Аппаратный делитель.
Операционный блок обрабатывает операнды, расположенные исключительно в
регистрах общего назначения процессора r0÷r15. Они являются своеобразным
«регистровым окружением» ЦПУ. Перед выполнением любой операции в регистрыисточники должны быть предварительно загружены исходные данные – операнды.
Результат операции также сохраняется в одном из «регистров окружения ЦПУ» r0÷r15
(см. черные стрелки с выходов операционных блоков на рис. 7.1).

95

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M

1. Инициализация
процессора

2. Непосредственная
загрузка данных

Регистр команд

3. Межрегистровый
обмен

Регистры общего назначения
(регистровый файл) r0÷ r15

3. Межрегистровый
обмен
4. Межрегистровый
обмен

r0
r1
r2

r12
r13-SP
r14-LR
r15-PC

PSP
MSP

Сдвиговый
7. Арифметические и
регистр
логические операции

АЛУ

5. Обмен с
памятью

2. Непосредственная
загрузка данных

Регистры модуля FPU
вычислиний
с плавающей точкой
s0÷ s31

Регистр управления Control

6. Обмен с
памятью

s0
s1
s2
s3
s4
s5
s6
s7

d0

s28
s29
s30
s31

d15

d1
d2
d3

Память

d16
8. Операции с
плавающей точкой

Умн-ль

Сопр-р
FPU

Дел-ль
9. Межрегистровый
обмен

Регистр статуса программы - PSR
Приложения Системы прерываний/исключений Системы выполнения
APSR
IPSR
EPSR

Регистр статуса
и управления FPU
FPSCR

Рис. 7.1 Программная модель процессора Cortex-M

96

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
Ниже (табл. 7.2) представлен список регистров окружения ЦПУ с указанием их
альтернативных обозначений и краткими рекомендациями по использованию.
Таблица 7.2 Регистры общего назначения
Имя
регистра
R0
R1
R2
R3
R4
R5
R6
R7
R8
R9
R10
R11
R12
R13

Второе
обозначение
SP

R14

LR

R15

PC
PSR

Использование
Младший банк регистров
общего назначения ЦПУ

Нет
никаких
ограничений
в
использовании
этих
регистров
в
качестве операндов-источников или
операндов-приемников

Старший банк регистров
общего назначения ЦПУ

Для
некоторых
команд
имеются
ограничения в использовании (см.
приложение 1).

Stack pointer register – регистр-указатель стека
Состоит из двух регистров:
PSP
SP_process
– указатель стека процесса
пользователя (программ нижнего уровня)
MSP
SP_main – указатель стека обработчиков
исключений и программ верхнего уровня
Link register – линк-регистр, регистр связи
Содержит адрес возврата в основную программу после команды
вызова подпрограммы
Program counter – программный счетчик (счетчик команд) Всегда
содержит адрес очередной, подлежащей выполнению команды.
Program Status Register – Регистр статуса программы
Состоит из трех регистров:
APSR Application Program Status Register – регистр статуса
программы-приложения
IPSR
Interrupt Program Status Register – регистр статуса
системы прерываний/исключений
EPSR
Execution Program Status Register – регистр статуса
системы выполнения команд

Дальше (см. 7.6) мы подробно остановимся на особенностях применения каждого
из этих регистров. В программе на Ассемблере зарезервированные имена регистров
можно вводить как в верхнем (R0÷R15), так и в нижнем (r0÷r15) регистрах (в общем
случае это зависит от версии конкретного языка Ассемблер).

7.4 Регистровое окружение сопроцессора
Модуль поддержки вычислений с плавающей точкой FPU может выполнять
операции только над операндами, расположенными в его регистрах окружения. Будем
называть их регистрами сопроцессора или регистрами модуля FPU. Как показано на рис.
7.1, имеется 32 регистра s0÷s31, предназначенных для хранения данных в формате с
плавающей точкой однократной точности. Все регистры 32-разрядные. Каждая пара
регистров s0÷s31 с точки зрения программиста может рассматриваться как один 64разрядный регистр. Он может содержать данные в формате с плавающей точкой двойной
точности или два слова с плавающей точкой однократной точности. Всего в окружении
сопроцессора 16 таких регистров с именами d0÷d15.

97

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
При написании программы имена регистров сопроцессора, как и имена регистров
ЦПУ, можно набирать как в нижнем (s0÷s31 или d0÷d15), так и в верхнем регистрах
(S0÷S31 или D0÷D15).
Сопроцессору и технологии вычислений с плавающей точкой посвящены
специальные разделы книги (главы 19, 20, 21).

7.5 Регистр управления Control
Регистр специального назначения, который позволяет установить нужный режим
работы процессора, задать тип доступа к памяти (привилегированный или
непривилегированный), разрешить или запретить работу встроенных в процессор
устройств, в частности, модуля FPU поддержки вычислений с плавающей точкой.
Обычно пользователей вполне устраивают значения в этом регистре «по
умолчанию» (которые устанавливаются автоматически при сбросе процессора Reset или
при включении питания). Содержимое этого регистра и технология его программирования
рассматриваются при описании файла начальной инициализации системы Startup (см. 9.1).

7.6 Влияние RISC-архитектуры процессора на его
программную модель
Во всех архитектурах современных процессоров в состав процессорного ядра
входит сверхоперативная память, содержащая регистры общего назначения. В
процессорах Cortex-M такая память имеется как в окружении центрального процессора,
так и в окружении сопроцессора. Особенность процессоров с RISC- архитектурой в том,
что их система команд поддерживает вычислительные операции исключительно с
регистрами окружения и не поддерживает операции с данными, расположенными в
памяти (кроме операций пересылки данных).
Операционный блок процессора и сопроцессор оптимизированы на работу с
регистрами окружения. В кодах команд при доступе к регистрам окружения процессора и
сопроцессора достаточно указать их внутренние адреса, а не 32-разрядные адреса ячеек
памяти, как это было бы при расположении операндов в памяти. Так, для адресации 16
регистров общего назначения ЦПУ достаточно использовать поле адреса всего из четырех
бит (адреса от 0 до 15). Следовательно, для задания двух адресов регистров-источников и
одного адреса регистра-приемника в формате команды достаточно 4*3=12 бит. При этом
остальные биты (в 16- или 32-разрядной команде) могут использоваться для задания кода
операции. Такой способ адресации операндов в команде называется регистровой
адресацией. Он позволяет создавать короткие команды (16 или 32 бита), быстро
считывать их из кодовой памяти и выполнять. При этом никакого обращения к внешней
памяти программ или данных за операндами не требуется.
Как доказывает успех фирмы ARM, именно такой подход позволяет создать
максимально компактную систему команд и обеспечить максимально высокую
производительность вычислительного процесса в конвейерных архитектурах процессоров
(глава 6).
Естественно, что ввиду ограниченности числа регистров ЦПУ и сопроцессора
обращения к памяти неизбежны. Однако, они выполняются помимо операционного блока
процессора через также оптимизированный по быстродействию интерфейс процессора с
памятью данных. При этом используется эффективный механизм косвенной адресации
памяти по содержимому любого регистра общего назначения процессора, в том числе
счетчика команд PC (относительная адресация) и указателя стека SP (стековая адресация).
Любой регистр общего назначения ЦПУ может выполнять функцию регистра-указателя
98

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
адреса расположения данных в памяти. Это чрезвычайно эффективный механизм доступа
к памяти. Он поддерживает пост- и пред- автоинкрементную и автодекрементную
адресацию, а также групповую загрузку/сохранение содержимого сразу нескольких
регистров (глава 11).
Таким образом, архитектура процессора Cortex-M и его система команд
эффективно поддерживают все три стадии вычислительного процесса:
1. Загрузка регистров ЦПУ или сопроцессора исходными данными:
1.1. Непосредственная – константами, расположенными в формате самой команды
(поз. 2 на рис. 7.1). Число возможных констант ограничено, так как применяется
специальный механизм «сжатия» данных;
1.2. Из других регистров окружения ЦПУ (поз. 3) или сопроцессора (поз. 4), уже
содержащих нужные данные;
1.3. Из кодовой памяти или памяти данных (поз. 5 и 6). Естественно, что эти данные
должны быть предварительно размещены там, для чего используются
специальные директивы Ассемблера.
2. Реализация вычислительного алгоритма – арифметических или логических
операций над целыми числами и числами с фиксированной точкой в операционном
блоке ЦПУ (поз.7) или над числами в формате с плавающей точкой – в сопроцессоре
(поз. 8).
3. Сохранение результатов вычисления (при необходимости) в оперативной памяти
микроконтроллера (поз. 5 и 6).
Особенность процессорных ядер Cortex-M в том, что в процессе пересылки данных
возможна попутная обработка этих данных, например, преобразование числа из формата
с фиксированной точкой в формат числа с плавающей точкой. Поэтому, стрелки на рис.
7.1, иллюстрирующие межрегистровый обмен, показаны условно. На самом деле данные в
процессе пересылки могут подвергаться обработке в операционном блоке ЦПУ или в
сопроцессоре.
Еще раз внимательно изучите рис. 7.1. По существу, он содержит классификацию
команд процессора по их функциональному назначению. Мы будем придерживаться этой
классификации при изучении команд процессора в последующих главах книги.

7.7 Назначение и особенности использования регистров
окружения ЦПУ
7.7.1 Регистры общего назначения
Регистры младшего регистрового банка (R0÷R7) не имеют каких-либо
ограничений по использованию и могут применяться в качестве операндов в любых
командах процессора. Использование регистров старшего банка (R8÷R12) в некоторых
командах не допускается. Поэтому, при написании своих программ, старайтесь, по
возможности, обойтись регистрами младшего регистрового банка.
В процессорах Cortex-M два набора команд, существовавших в предыдущих
версиях процессоров этой фирмы (набор 32-разрядных команд ARM и набор 16разрядных команд Thumb), объединены в единый набор команд Thumb2. При этом
никаких программных переключений из одного набора команд в другой, как это было
ранее, не требуется. Транслятор сам выбирает оптимальный способ кодирования
команды: если он сможет сгенерировать более компактную 16-разрядную команду –
делает это, в противном случае – генерирует 32-разрядную.
99

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
Транслятор с Ассемблера является «интеллектуальным» и
оптимизирует объем программного кода. Команды набора ARM (32разрядные) генерируются, как правило, для много-операндных команд и
команд, использующих в качестве операндов регистры старшего
регистрового банка.

7.7.2 Счетчик команд PC. Относительная адресация в командах передачи
управления
Регистр R15 в процессорах Cortex-M выполняет специальную функцию –
определяет порядок выполнения команд в программе и называется счетчиком команд PC
(Program Counter) или программным счетчиком. Он всегда содержит адрес очередной
команды, подлежащей выполнению, то есть адрес команды, которая уже считана из
кодовой памяти на конвейер команд и прошла первые две стадии обработки на конвейере
– выборку и дешифрацию. Работа конвейера команд описана нами ранее (см. 4.4).
Так как в унифицированном наборе команд Thumb2 присутствуют и 32-разрядные
и 16-разрядные команды, то в зависимости от формата конкретной команды, в процессе ее
выполнения, счетчик команд PC автоматически инкрементируется на +4 или на +2 (размер
слова или полуслова в байтах).
Большая часть любого программного кода выполняется последовательно. Однако,
часто требуется либо безусловная передача управления в другую точку программы, либо
условная передача управления – по некоторому условию, которое может быть истинным
или ложным в зависимости от результата предыдущих вычислений. В этих случаях
счетчик команд PC с помощью соответствующих команд загружается адресом точки
входа в нужный фрагмент программы, и выполнение программы продолжается с нового
адреса.
Точка программы, в которую должен быть выполнен переход, помечается меткой.
Значение метки соответствует адресу размещения в памяти соответствующей команды и
автоматически идентифицируется транслятором с Ассемблера. Для безусловной передачи
управления в процессорах ARM используется команда B label, где мнемокод «B»
соответствует английскому слову branch (ответвление, передача управления), а «label» –
является именем метки, содержащим адрес точки входа в нужный фрагмент программы.
Вы скажете, а как же 32-разрядный адрес ячейки памяти помещается в 32-разрядной
команде? Ведь, в этом случае, не остается ни одного бита для размещения собственно
кода операции? Может быть, такая команда будет занимать в кодовой памяти не одно, а
целых два слова – 64 бита? Но, это противоречит концепции RISC-архитектуры
процессора, в соответствии с которой все команды должны быть не длиннее одного слова.
Разработчики ARM-процессоров для исключения этого противоречия предложили
в командах передачи управления использовать не прямую адресацию (когда адрес
перехода указывается отдельным словом в формате команды), а относительную
адресацию по содержимому счетчика команд PC и дополнительному короткому
смещению #offset, которое должно присутствовать в формате таких команд, как
непосредственная константа.
Суть метода иллюстрирует рис. 7.2. В начале выполнения любой команды счетчик
команд PC автоматически инкрементируется так, что всегда показывает на адрес
очередной команды, подлежащей выполнению. Если по адресу M1 находится команда
безусловной передачи управления B M5, то в начале ее выполнения значение счетчика
команд будет равно M2 (адресу следующей команды в программе), то есть будет известно
процессору. Транслятор с Ассемблера, «зная» адрес метки M5, на которую должен быть
сделан переход, и адрес метки M2, может, на стадии ассемблирования программы,
100

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
рассчитать величину смещения offset = (M5-M2) и записать его в виде непосредственного
операнда #offset в формат команды B M5.
На стадии выполнения команды смещение
просто добавляется к текущему значению счетчика
Основная
команд: (PC) = M2+(M5-M2) = M5, в результате
программа
чего счетчик команд перезагружается нужным
адресом перехода addr = (PC) + #offset. Операции
M1 B M5
(PC) = M2 по расчету величины смещения и кодированию
команды перехода полностью берет на себя
M2
транслятор с Ассемблера. Вы, как программист,
Offset = M5-M2 должны лишь поставить метку в нужном месте
программы и написать команду перехода на нее B
M5
label.
Addr = (PC)+Offset=M2+(M5-M2)=M5
Передача управления может выполняться
как в направлении «Вперед» (в сторону
возрастающих адресов), так и в направлении
«Назад» (в сторону убывающих адресов), что
M9 B M2
необходимо при организации в программе циклов.
В этом случае транслятор с Ассемблера закодирует
в команде перехода отрицательное значение
смещения (см. команду B M2 на рис. 7.2).
Рис. 7.2 Передача управления
по содержимому счетчика команд
PC и непосредственному смещению
#offset.
Будьте осторожны при использовании регистра R15 (PC)! Хотя он
является регистром общего назначения и в большинстве команд может
использоваться как регистр-источник или регистр-приемник данных,
помните, что в последнем случае это вызовет передачу управления по
адресу, равному новому содержимому R15. Вы можете случайно
запрограммировать передачу управления, причем «в никуда»! По этому
адресу может вообще не быть кода – поведение Вашей программы будет
непредсказуемым!
Имейте также в виду, что в некоторых командах использование регистра R15(PC) в
качестве операнда-приемника и даже операнда-источника – запрещено.
При переходах с использованием относительной адресации по текущему значению
счетчика команд возможны «короткие переходы» (адресное смещение в диапазоне ±1024)
и «средние переходы» (адресное смещение ±4096). Выбор нужного кода операции
перехода транслятор делает автоматически.
При необходимости «длинного перехода» на удаленную метку придется
воспользоваться командой непосредственной загрузки счетчика команд 32-разрядным
адресом перехода (глава 10).

101

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
На какой адрес области памяти всегда показывает содержимое счетчика
команд PC?
1) Чему окажется равно содержимое счетчика команд PC после выполнения
команды B M2 (см. рис. 7.2)?
1) На адрес следующей, подлежащей выполнению команды.
2) Адресу метки M2.

7.7.3 Регистр-указатель стека. Понятие стека.
Регистр R13 имеет особое значение. Он является регистром-указателем стека SP.
Стеком называется специально выделенная для временного хранения информации
линейная область памяти данных (ОЗУ). Она используется как для сохранения адресов
возврата в основную программу из подпрограмм (функций), так и для временного
хранения любых других данных из регистров процессора. Одно из основных назначений
стека – сохранение контекста основной программы путем записи значений некоторых
нужных регистров в стек в начале подпрограммы или процедуры обработки
прерывания/исключения и восстановление контекста перед операцией возврата из
подпрограммы или процедуры обработки прерывания/исключения в основную
программу. Это позволяет внутри таких процедур «безбоязненно» использовать эти
регистры.
Указатель стека SP содержит адрес последней, «занятой» данными ячейки памяти
в области стека. При очередной записи данных в стек указатель стека предварительно
декрементируется, указывая на адрес очередной свободной ячейки памяти, после чего в
нее записываются нужные данные. Все команды работы со стеком выполняют запись и
извлечение из стека только полных 32-разрядных слов (4-байтовых), поэтому указатель
стека всегда модифицируется на -4 (при записи) и на +4 (при чтении). Напомним, что в
процессорах ARM собственный адрес имеет каждый байт памяти.
Стек такого типа называется LIFO (Last In, First Out) – «Последний вошел, первый
вышел». Его принцип действия аналогичен «магазину» патронов пистолета или автомата:
последний, поступивший в «магазин» патрон, извлекается в ствол первым.
Порядок записи данных в стек или извлечения из него определяет текущее
содержимое указателя стека SP. Во всех командах работы со стеком значения указателя
стека SP модифицируется автоматически. На рис. 7.3 иллюстрируется принцип работы
стека и авто-модификации содержимого указателя стека. Предполагается, что
выполняются четыре операции сохранения в стеке значений переменных VAR1, VAR2,
VAR3 и VAR4, расположенных в регистрах общего назначения ЦПУ (команды PUSH
{Rn}) и две операции извлечения из стека ранее записанных данных (команды POP {Rn}).
Использование в поле операндов команд PUSH {Rn} и POP {Rn} фигурных скобок
означает задание списка регистров Reglist, содержимое которых должно быть сохранено в
стеке или восстановлено из него. По существу, это команды группового
сохранения/восстановления данных (более подробно в главе 15).
При включении питания или сбросе процессора указатель стека SP
автоматически инициализируется начальным значением, задающим положение так
называемой вершины стека. В примере на рис. 7.3 это значение равно 0x20005000
(находится в конце области памяти данных в соответствии с унифицированной картой
памяти), следовательно, все ячейки памяти с большими адресами считаются «как бы
занятыми», а с меньшими – «свободными».

102

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M

Запись в стек
Адрес

Чтение из стека

Занято
SP

0x2000 5000

VAR1

SP

VAR1

VAR1

VAR1

VAR1

VAR1

VAR2

VAR2

VAR2

VAR2

VAR3

VAR3

0x2000 4FFC

VAR2

SP

SP

0x2000 4FF8

VAR3

SP

SP

0x2000 4FF4

VAR4

SP

0x2000 4FF0

Свободно
0x2000 4FEC

0x2000 0008

0x2000 0004

0x2000 0000

Рис. 7.3 Запись и извлечение данных из стека
Как видите, при записи в стек значение в указателе стека SP автоматически
декрементируется (-4) – стек заполняется данными в направлении убывающих адресов
памяти – это так называемый автодекрементный стек по записи. При извлечении данных
из стека в регистры ЦПУ вначале выполняется считывание данных, после чего указатель
стека автоматически инкрементируется (+4), как бы высвобождая текущую ячейку памяти
для новой записи (ранее записанное в нее значение сохраняется неизменным, пока не
состоится новая запись).
В действительности указатель стека SP состоит из двух регистров: PSP – указателя
стека процесса пользователя и MSP – указателя стека обработчиков исключений. К
какому именно указателю стека в данный момент обращается процессор, зависит от
текущего режима работы процессора (непривилегированный или привилегированный).
Это позволяет физически иметь два стека: стек для пользовательских программ (нижнего
уровня), работающих в непривилегированном режиме, и стек для программ более
высокого уровня – операционных систем реального времени и мониторов, работающих в
привилегированном режиме. Такое разделение стеков обеспечивает независимость
программ разного уровня друг от друга и повышает надежность микропроцессорной
системы в целом.
1) Возможна ли запись и извлечение из стека байта, полуслова?
2) Как понимать термин «автодекрементный стек по записи»?
3) Можно ли использовать указатель стека SP в качестве обычного
операнда в командах?

103

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
1) Нет, только 32-разрядного слова целиком.
2) Это означает, что перед записью очередных данных в стек значение
указателя сначала декрементируется (на 4) и только затем выполняется
запись.
3) Да, в большинстве команд. Только для некоторых команд
использование указателя стека SP и счетчика команд PC ограничивается.

7.7.4 Регистр связи. Внутренняя модульность программного обеспечения.
Особую роль при программировании играет регистр R14, который называется
регистром связи или линк-регистром LR. Понятие внутренней модульности программы,
написанной на Ассемблере и расположенной в отдельном файле, предполагает, что она
состоит из основной программы и некоторого числа подпрограмм (функций), которые
могут вызываться из основной программы в произвольном порядке любое число раз.
Каждая подпрограмма предварительно отлаживается на всем возможном наборе
значений аргументов. Перед вызовомподпрограммы ей передаются конкретные значения
аргументов, после чего следует вызов подпрограммы. Технология передачи параметров в
подпрограммы и особенности работы с вложенными подпрограммами (одна вызывает
другую) рассматривается отдельно (глава 15).
В большинстве процессоров для вызова подпрограмм используется специальная
команда типа CALL addr, которая вначале запоминает в стеке следующий после этой
команды адрес, называемый адресом возврата, а затем загружает в счетчик команд PC
значение addr – адрес точки входа в подпрограмму. В результате чего управление
передается подпрограмме. В конце подпрограммы обычно используется команда RET
(Возврат), которая извлекает из стека адрес возврата и записывает его в счетчик команд
PC. Тем самым управление возвращается в основную программу.
Отличительной особенностью процессоров ARM является то, что в их архитектуре
для сохранения адреса возврата используется не стек, расположенный во внешней по
отношению к процессору памяти данных, а специально предназначенный для этой цели
регистр общего назначения – регистр связи LR, который можно рассматривать как
регистр обратной связи при вызове подпрограмм. Преимуществом такого подхода
является то, что адрес возврата сохраняется не в памяти, доступ к которой может быть
медленным, а в одном из регистров окружения ЦПУ, доступ к которому – предельно
быстрый,
что
обеспечивает
существенное
повышение
вычислительной
производительности процессора при работе с подпрограммами.
Рис. 7.4 иллюстрирует механизм вызова подпрограмм и возврата в основную
программу, реализуемый с использованием двух регистров ЦПУ – регистра связи LR и
счетчика команд PC. В отличие от простого перехода по метке (команда B label) для
вызова подпрограммы используется команда передачи управления с обратной связью
Branch with link – BL label: сначала адрес возврата (адрес следующей команды в
программе) сохраняется в регистре связи LR, а затем счетчик команд PC загружается
адресом метки label.
Последней командой подпрограммы должна быть команда косвенной загрузки
счетчика команд PC (Branch indirect) текущим содержимым регистра связи LR – BX LR.
Это аналог команды RET («Возврат») в других процессорах. В мнемокоде этой команды
первая буква «B» от англ. Branch (ответвление), а вторая от англ. Exchange (обмен). В
команде выполняется «как бы обмен» данными между двумя регистрами, один из которых
указан в качестве операнда-источника – LR, а второй – «по умолчанию» является
регистром-приемником – счетчиком команд PC.

104

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
В общем случае команда косвенной передачи управления имеет следующий
синтаксис:
BX Rm
По написанию она является одно-операндной командой – переслать данные из
регистра-источника Rm в счетчик команд PC, имя которого в качестве регистраприемника опущено и предполагается «по умолчанию». Это действительно косвенная
передача управления по адресу, расположенному в регистре общего назначения Rm. Если
операндом-источником является регистр связи LR, то сохраненный в нем ранее адрес
возврата загружается в счетчик команд – выполняется возврат в основную программу.
Основная
программа

Регистр
связи
LR

Счетчик
команд
PC

M1 BL FUNC_1
M2

M2

FUNC_1

M3 BL FUNC_2
M4

M4

FUNC_2

M5 BL FUNC_1
M6

M6

FUNC_1

M2

M2

M6

M6

M4

M4

FUNC_1
Подпрограмма

BX LR

FUNC_2

На самом деле мнемоника команды не
совсем точна. Полноценного обмена данными
PC↔Rm не происходит, выполняется обмен
только в одну сторону PC←Rm.
На Рис. 7.4 красными стрелками показано
состояние регистра связи LR и счетчика команд PC
после выполнения команд вызова подпрограмм BL
label и команд BX
LR возврата из
подпрограммы в основную программу.
К любой подпрограмме можно обращаться
любое число раз (на Рис. 7.4 – к подпрограмме
FUNC_1).

Подпрограмма

BX LR

Рис. 7.4 Внутренняя модульность
программного обеспечения
Пара команд BL Label и BX LR выполняет в процессорах ARM
функции вызова подпрограммы Call Label и возврата из нее в основную
программу Ret. Вызов и возврат выполняются предельно быстро, так как
для сохранения адреса возврата используется не стек в памяти данных, а
специальный регистр обратной связи LR, являющийся регистром общего
назначения ЦПУ.
При использовании вложенных подпрограмм для сохранения адресов возврата все
же придется использовать стек (глава 15).
1)
Можно ли вместо команды BX LR использовать команду BX R14?
2)
А команду пересылки данных из регистра-источника LR в регистрприемник PC: MOV PC, LR или ее аналог MOV R15, R14?
3)
Как Вы думаете, что означает команда BLX Rm?
1)
Да, R14 всего лишь альтернативное обозначение регистра связи
LR.
2) Да, оба варианта. Однако, привыкайте использовать общепринятую в процессорах
ARM мнемонику команды возврата из подпрограммы BX LR.
3) Так как в ее мнемокоде есть корень BL, можно предположить, что это вызов
подпрограммы с сохранением адреса возврата в регистре связи LR. Суффикс X
указывает на то, что новая загрузка счетчика команд будет не по адресу метки label,
как в команде BL label, а по адресу, расположенному в регистре Rm, указанному в
105

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
поле операндов команды («с обменом»). Так что это команда косвенной передачи
управления по содержимому регистра Rm с одновременным сохранением адреса
возврата в регистре связи LR (Branch indirect with Link).
Исторически команды передачи управления использовались в
процессорах ARM также для переключения между возможными двумя
наборами команд ARM и Thumb. Переключение выполнялось младшим
битом адреса перехода. В процессорах Cortex-M используется единый
набор команд Thumb-2. Однако, младший бит адреса перехода попрежнему отвечает за текущий набор команд процессора. Поэтому, при выполнении
косвенных переходов приходится следить за тем, чтобы он был равен 1 (набор Thumb).
Переход всегда будет выполняться по правильному адресу – с автоматическим
очищением младшего бита адреса перехода.

7.8 Регистр статуса программы. Флаги результатов
операций в операционном блоке ЦПУ
7.8.1 Регистр статуса программы и его компоненты
Любой вычислительный процесс и, тем более, процесс управления оборудованием,
предполагает анализ текущих значений переменных (больше, меньше, равно и т.д.), а
также состояния внешних датчиков и оперативное принятие решения: «Что делать
дальше?». Поэтому, важнейшим элементом любого операционного блока процессора
является так называемый регистр признаков результатов операций или регистр флагов
операций.
В процессорах Cortex-M этот регистр называется регистром статуса программыприложения APSR (Application Program Status Register). Вместе с регистром статуса
системы прерываний и исключений IPSR (Interrupt Program Status Register), а также
регистром статуса системы выполнения команд EPSR (Execution Program Status Register)
эти три регистра образуют один 32-разрядный регистр статуса программы PSR (Program
Status Register).
На рис. 7.5 показано распределение битовых полей между тремя регистрами
статуса, приведены символические названия этих полей.
Регистр статуса программы PSR
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9

8

7

6

5

4

3

2

1

0

8

7

6

5

4

3

2

1

0

8

7

6

5

4

3

2

1

0

3

2

1

0

Регистр статуса программы приложения APSR
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9

N Z C V Q
Регистр статуса системы прерываний/исключений IPSR
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9

ISR_NUMBER
Регистр статуса системы выполнения EPSR
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9

IC/IT T

8

7

6

5

4

IC/IT

Рис. 7.5 Регистр статуса программы PSR и его три составляющие APSR, IPSR, EPSR

106

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
Отметим, что несмотря на то, что все три регистра статуса являются частью одного
и того же регистра PSR, в Ассемблерной программе к каждому из них можно обращаться
по его персональному имени: APSR, IPSR или EPSR. Возможно и обращение по
символическому имени PSR. При этом будут считаны состояния всех трех статусных
регистров одновременно.
При чтении содержимого статусных регистров в регистры общего назначения ЦПУ
будут считаны только те битовые поля, которые относятся к указанному Вами регистру.
Отметим также, что регистры статуса являются регистрами специального назначения.
Доступ к ним возможен только с помощью специальных команд обмена данными между
регистрами общего назначения ЦПУ и регистрами специального назначения (эти команды
рассматриваются в последующих главах).

7.8.2 Флаги результатов операций в ЦПУ
Регистр статуса приложения APSR содержит пять флагов результатов операций,
расположенных в самых старших битах регистра APSR – (31÷27) (см. табл. 7.3):
Таблица 7.3 Описание флагов
Флаг

Название. Порядок формирования

N

Флаг отрицательного результата N (Negative). Устанавливается в 1 (N=1), если в
результате операции получено число, старший бит которого (31-й, знаковый) равен 1.
Если старший бит равен нулю, то флаг N сбрасывается в 0 (N=0).
Флаг нуля Z (Zero). Если в результате арифметической или логической операции получен
нулевой результат (все 32 бита результата нулевые), то флаг выставляется (Z=1), в
противном случае – сбрасывается (Z=0).
Флаг переноса C (Carry).
Устанавливается (C=1), если в результате операции сложения двух 32-разрядных чисел
произошел перенос за пределы разрядной сетки. В противном случае – сбрасывается
(C=0). Свидетельствует о переполнении при сложении чисел без знака.
Сбрасывается (C=0), если в результате операции вычитания двух 32-разрядных чисел
произошел «заем» и выставляется (C=1) в противном случае (нет «заема»).
Флаг переполнения V (Overflow). Выставляется (V=1) в том случае, когда при
выполнении
арифметической
операции
сложения
или
вычитания
чисел,
рассматриваемых процессором как числа со знаком в дополнительном коде, возникло
переполнение, т.е. полученный 32-разрядный результат вышел за пределы диапазона 32разрядных чисел со знаком, в противном случае – сбрасывается (V=0).
Флаг насыщения Q (saturation). Вырабатывается только двумя командами процессора –
насыщение содержимого регистра как числа без знака USAT и насыщение содержимого
регистра как числа со знаком SSAT. Если насыщение (ограничение на уровне
максимального значения) имело место, то флаг устанавливается (Q=1), в противном
случае – сбрасывается (Q=0).

Z

C

V

Q

Мы уже отмечали (глава 3), что операции двоичного сложения/вычитания чисел
без знака и чисел со знаком в дополнительном коде выполняются в АЛУ одними и теми
же командами. Более того, процессор не может отличить число без знака от числа со
знаком в дополнительном коде. О том, с какими числами в данный момент выполняется
операция сложения или вычитания знает только программист. Результат операции всегда
будет правильным, если он не выходит за пределы разрядной сетки процессора (в данном
случае 32-разрядной). О выходе за эти пределы при работе с числами без знака
свидетельствует флаг переноса C, а при работе с числами со знаком – флаг переполнения
V. Поэтому, процессор всегда формирует оба флага (и C, и V). Нужный флаг
анализируется программистом исходя из того, с какими числами он работает в данный
момент (со знаком или без знака).

107

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
1)
Работаем с числами без знака. Какое значение должна превысить
сумма, чтобы возник флаг переноса С?
2)
Работаем с числами со знаком в дополнительном коде. В каком
диапазоне должен находиться результат операции сложения или
вычитания целых чисел, чтобы флаг переполнения V не возник?
3)
Когда устанавливается флаг нулевого результата Z?
Максимальное целое число без знака (232-1)= 4,294,967,295
В диапазоне -(231)÷(231-1), то есть от -2,147,483,648 до
+2,147,483,647
3)
Тогда, когда результат тождественно равен нулю, т.е. во всех
двоичных разрядах, без исключения, нули.
1)
2)

7.8.3 Программное управление формированием флагов результатов
операций
В большинстве процессоров флаги результатов операций формируются
автоматически после завершения каждой арифметической или логической операции. В
процессорах ARM – это не так. Они выставляются только тогда, когда программист
этого требует. Если такого требования нет, то флаги не модифицируются (их значения
остаются прежними).
С какой целью это делается? Для повышения надежности программного кода.
Если программист желает выполнить команду условной передачи управления (или
условно выполнить одну или группу команд), то он должен в явном виде указать, по
результатам какой именно операции должны быть установлены флаги. Это повышает
ответственность программиста за написанный им код: прежде чем написать команду,
которая может существенно изменить ход вычислительного процесса или процесса
управления, необходимо определиться, на основании результатов какой конкретной
операции это изменение должно быть сделано.
Указание на установку флагов делается путем добавления к мнемонике любой
команды суффикса «S» (Set – Установить флаги). Итак, чтобы заставить процессор
сформировать флаги результатов операции в регистре APSR (N, Z, C, V, Q), нужно
добавить к основному мнемокоду команды суффикс «S»: . Ниже в качестве примера показана команда сложения двух операндов в
регистрах r3 и r4 с сохранением суммы в регистре r0 в двух вариантах: 1) без установки
флагов результата операции сложения; 2) с установкой флагов:
ADD r0, r3, r4
ADDS r0, r3, r4

; Без установки флагов
; C установкой флагов

Здесь и далее в книге суффиксы, которые модифицируют операцию, будут
выделяться цветом. Это делается исключительно в учебных целях. При написании
реальных программ цветовое деление внутри мнемокода команды не используется.
Единственным исключением из приведенного выше правила «принудительного»
формирования флагов результатов операций являются команды сравнения CMP и
тестирования: TST (Логическое И с маской); TEQ (тестирование на эквивалентность –
Логическое Исключающее ИЛИ двух операндов). Команда сравнения предназначена
только для того, чтобы сравнить два операнда путем вычитания из первого операнда
второго. По результату операции сравнения выставляются флаги в регистре APSR, а сам
результат вычитания нигде не сохраняется (отбрасывается). Приведенные ниже две

108

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
команды с точки зрения установки флагов эквивалентны. Разница заключается в том, что
в команде вычитания результат вычитания сохраняется в регистре-приемнике r1, а в
команде сравнения – просто теряется:
SUBS r1, r2
CMP r1, r2

; [(r1)-(r2)] → r1, установить флаги
; [(r1)-(r2)], установить флаги

Обратите внимание на то, что в команде вычитания программист сделал явное
указание процессору на необходимость формирования флагов результата операции с
помощью дополнительного суффикса «S». В команде сравнения этот суффикс не
требуется. Заметьте, что в примере использована двух-операндная команда вычитания.
Функцию регистра-приемника автоматически выполняет первый операнд-источник
(регистр r1).

7.9 Условная передача управления и условное выполнение
команд
Процессоры фирмы ARM имеют одну очень важную особенность, которая
принципиально отличает их от процессоров других фирм – большинство команд,
входящих в систему команд и выполняемых как в операционном блоке ЦПУ, так и в
сопроцессоре, может выполняться условно. Такие команды будут иметь мнемокод,
расширенный специальным кодом условного выполнения cond:

Код условного выполнения представляет собой суффикс, который добавляется к
мнемокоду команды без каких-либо пробелов или разделителей. Если команда уже имеет
в своем обозначении какой-то/какие-то суффиксы, то код условного выполнения
указывается после них (в самом конце мнемокода команды). В справочнике по системе
команд те команды, которые могут быть выполнены условно, имеют в описании
синтаксиса опциональный код условного выполнения {cond}.

7.9.1 Коды условной передачи управления и условного выполнения команд
Ветвление программы или условное выполнение команд возможно в следующих
случаях:
1) Сразу после любой арифметической или логической операции, в мнемокоде которой
присутствует суффикс «S» (установить флаги результата операции), или после любого
числа следующих за ней команд, которые сами не модифицируют флаги.
2) Сразу после операции сравнения 32-разрядных чисел с использованием команды
сравнения CMP или одной из команд тестирования TST, TEQ;
3) После сравнения чисел в формате с плавающей точкой командой VCMP.F32 и
дополнительного копирования флагов из регистра FPSCR (статуса и управления
модуля плавающей точки), в регистр статуса программы приложения APSR – для
этого предусмотрена специальная команда (глава 21).
Коды условного выполнения строго соответствуют определенным состояниям
флагов результатов операций – либо отдельных флагов, либо их комбинации. Это
соответствие приведено в табл. 7.4 с указанием значения суффикса при условии, что была
выполнена операция сравнения двух 32-разрядных чисел. Заметим, что по результату
сравнения чисел в формате с плавающей точкой также можно использовать указанные
109

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
ниже суффиксы (они общие для ЦПУ и для сопроцессора). Для этого необходимо
предварительно скопировать флаги результата сравнения чисел в формате с плавающей
точкой из регистра FPSCR сопроцессора в регистр APSR – подробно в главах 20, 21.
Таблица 7.4 Суффиксы условного выполнения команд и их значение
Суффикс

Флаги

Значение

EQ
NE
HS
CS

или

Z=1
Z=0
C=1

или

C=0

«Эквивалентно» (Equal)
«Не эквивалентно» (Not Equal)
«Выше или то же самое» (Higher or same) или
«Флаг Carry установлен» (Carry Set)
Больше или равно (≥) при сравнении чисел без знака (unsigned)
«Ниже»(Lower) или
«Флаг Carry сброшен» (Carry Clear)
Строго меньше () при сравнении чисел без знака (unsigned)
«Ниже или то же самое» (Lower or same)
Меньше или равно (≤) при сравнении чисел без знака (unsigned)
«Больше или эквивалентно» (Greater then or equal)
Больше или равно (≥) при сравнении чисел со знаком (signed)
«Меньше чем» (Less then)
Строго меньше () при сравнении чисел со знаком (signed)
«Меньше чем или эквивалентно» (Less then or equal)
Меньше или равно (≤) при сравнении чисел со знаком (signed)
«Всегда». Суффикс по умолчанию, который используется всегда,
когда код условного выполнения не специфицируется.

LO
CC
MI
PL
VS
VC
HI

N=1
N=0
V=1
V=0
(C=1) and (Z=0)

LS

(C=0) or (Z=1)

GE

N=V

LT

N!=V

GT

(Z=0) and (N=V)

LE

(Z=1) or (N!=V)

AL

Любые значения
флагов

Для сравнения переменных без знака и со знаком в дополнительном коде
используются разные суффиксы. Так, математическому символу «>» (Больше) для чисел
без знака соответствует суффикс «HI» (Выше), а для чисел со знаком? – символ «GT»
(Строго больше).
Процессор при выполнении операции сравнения (или вычитания) не «знает» с
какими числами он имеет дело: с числами без знака или со знаком в дополнительном коде.
В любом случае, на основании значений всего 4-х флагов N, Z, C, V он может оценить
истинность или ложность всех возможных условий сравнения (=, ≠, >, ≥ , ≤, < ) и для
чисел без знака и для чисел со знаком в дополнительном коде, формируя значения всех
кодов условного выполнения, приведенных в табл. 7.4. Программист, зная с какими
числами он работает, должен выбрать команду условного ветвления или условного
выполнения с соответствующим суффиксом:
• Для сравнения любых чисел на равенство или неравенство - EQ или NE
• Для оценки знака числа – MI или PL
• Для сравнения только чисел без знака - HS, LO, HI, LS (выделены зеленым)
• Для сравнения только чисел со знаком – GE, LT, GT, LE (выделены голубым)

110

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
1.
Символ восклицательного знака в таблице использован для задания
операции инверсии флага N!;
2.
Напомним, что при вычитании двоичных чисел флаг Carry (C) –
выполняет также функцию флага «заема». Если «заема» нет, то флаг C
устанавливается в 1, в противном случае сбрасывается в 0. Наличие флага
С свидетельствует о том, что при вычитании двух чисел без знака «заема»
нет, следовательно, первое число «Выше или равно», т.е. «≥» для чисел без знака;
3. Условие «GE» (Строго больше) при сравнении чисел со знаком в дополнительном коде
проверяется по равенству флагов отрицательного результата N и знакового
переполнения V: V=N. Приведем несколько простых примеров байтовых операций
вычитания, чтобы Вы могли убедиться в том, что такая проверка допустима
(результаты представлены для условно 8-разрядного АЛУ, хотя на самом деле оно 32разрядное) (см. табл. 7.5).
Таблица 7.5 Иллюстрация работы условия «GE»
Операция сравнения
чисел со знаком

Состояние флагов
NиV

Флаги эквивалентны?

Результат сравнения

(+3)-(+2)=+1
(+2)-(+3)=-1
(+3)-(+3)=0
(-1)-(-3)=+2
(-3)-(-1)=-4
+127-(-3)=+130
(-3)-(+127)=-130

N=0, V=0
N=1, V=0
N=0, V=0
N=0, V=0
N=1, V=0
N=1, V=1
N=0, V=1

Да
Нет
Да
Да
Нет
Да
Нет


<


<

<

4. Из этой же таблицы следует, что условие «LT» (Строго меньше) при сравнении
знаковых операндов можно проверять по взаимной инверсии флагов: N!=V
При выполнении операции сравнения или любой команды с
суффиксом обязательного формирования флагов «S» процессор
автоматически формирует значения всех четырех флагов N, Z, C, V,
состояние которых однозначно определяет все возможные результаты
сравнения чисел без знака и со знаком. Вслед за командой сравнения должна
применяться команда условного ветвления или условного выполнения с конкретным
суффиксом условного выполнения (с тем условием, которое проверяется программистом).

7.9.2 Блоки условного выполнения команд
В процессорах Cortex-M технология условного выполнения команд
усовершенствована по сравнению с ранее применявшейся в процессорах ARM: команды
условного выполнения (от одной до 4-х) автоматически помещаются транслятором с
Ассемблера в так называемый блок условного выполнения команд, который начинается
специальной командой условного выполнения блока IT (If-Then). Этот механизм, подробно
рассмотренный далее (см. 12.4), позволяет поднять скорость выполнения команд
условного выполнения и производительность процессора. Автоматическая генерация
блоков условного выполнения транслятором позволяет нам пока не вдаваться в детали
этого процесса. Заметим только, что блоки условного выполнения не генерируются для
команд условного ветвления программы. Они появляются только при условном
выполнении других операций (арифметических, логических, пересылки и т.д.).
Число условно выполняемых команд в одном блоке – не более четырех. Если
команд больше, то генерируется последовательность из нескольких блоков.

111

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M

7.9.3 Примеры условной передачи управления и условного выполнения
команд
Пример 1. Требуется сравнить два 32-разрядных числа без знака в регистрах r3 и
r4. Если число в регистре r3 окажется «Выше» (Higher), «>», чем число в регистре r4, нужно поменять местами содержимое регистров r3, r4.
Первое, что мы должны сделать – это сравнить числа в регистрах. Для этого
проще всего использовать команду сравнения, которая не требует суффикса «S»
установки флагов и выполняет их модификацию автоматически:
CMP r3, r4

; (r3)-(r4)

Решим задачу сначала с использованием команды условной передачи управления.
Так как мы имеем дело с числами без знака, то условию «Выше» соответствует суффикс
«HI». При выполнении этого условия мы должны поменять местами содержимое
регистров r3, r4, а в противном случае, оставить все «как есть». Соответствующий
фрагмент программы будет иметь вид:
; Вариант №1
BHI Change
B
Next

; Перейти на метку Change при условии HI
; В противном случае - продолжить программу

Сhange MOV r0, r3
MOV r3, r4
MOV r4, r0
Next

; Поменять местами содержимое
; в регистрах r3, r4
; Временно сохранить содержимое r3 в r0
;(r3) ← (r4)
;(r4) ← (r0)
; … Продолжение программы

Этот фрагмент программы хоть и решает задачу, но показывает, как «не надо»
программировать на Ассемблере! В нем используется сначала команда условного
перехода по нужному нам условию, а затем – команда безусловной передачи управления
на метку Next продолжения программы. Если этого не сделать, то блок кода, в котором
делается обмен содержимого регистров r3, r4, помеченный меткой Change, будет
выполняться при любом результате сравнения исходных чисел.
Как правильнее решать подобные задачи? Мы должны определить так называемое
оппозитное условие (противоположное). В нашем случае противоположным к условию
HI (>) «Выше» будет условие LS (≤) «Ниже или то же самое». Если оппозитное условие
будет истинным, нам ничего не нужно делать – просто перейти к следующему фрагменту
программы Next. В противном случае – нужно выполнить требуемый обмен данными:
; Вариант №2
BLS Next

Change MOV r0, r3
MOV r3, r4
MOV r4, r0
Next

;
;
;
;
;
;
;
;
;

Перейти на метку Next при условии LS
по оппозитному условию!
Поменять местами содержимое
в регистрах r3, r4
если условие не выполняется
Временно сохранить содержимое r3 в r0
(r3) ← (r4)
(r4) ← (r0)
… Продолжение программы

112

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
Покажем, как та же задача решается с использованием механизма условного
выполнения команд. Сразу после операции сравнения можно воспользоваться любыми
командами с нужными нам кодами условного выполнения:
; Вариант №3
CMP r3, r4

Change MOVHI r0, r3
MOVHI r3, r4
MOVHI r4, r0
Next

;
;
;
;

(r3)-(r4)
Поменять местами содержимое
в регистрах r3, r4
если условие HI истинно

;
;
;
;

Сохранить содержимое r3 в r0
(r3) ← (r4)
(r4) ← (r0)
… Продолжение программы

Как мы уже отмечали, транслятор автоматически добавит перед блоком условного
выполнения (в данном случае из трех команд), команду IT (ее назначение см. в 12.4). Если
условие HI «истинно», все три команды будут последовательно выполнены, в противном
случае – пропущены. Согласитесь, что последнее решение существенно проще и понятнее
программисту. Причем, как видите, можно вообще обойтись без команд условной или
безусловной передачи управления.
Возможности условного выполнения команд являются важным
преимуществом процессоров фирмы ARM. Они могут кардинально
изменить стиль программирования на Ассемблере, существенно уменьшив
число безусловных и условных переходов в программе. Это значит, что
конвейер команд практически не будет перезагружаться, что повысит
общую производительность системы.
1.
Нужна ли метка Change в двух последних вариантах? Можно ли ее
опустить?
2.
Как будет работать программа в варианте № 3, если условие HI не
выполняется?
3.
Что нужно изменить в приведенных выше версиях кода, если
сравниваться будут два 32-разрядных числа со знаком в дополнительном
коде?
1.
Не нужна. Ее можно опустить, но лучше этого не делать! Она
выполняет роль дополнительного комментария, структурирующего код.
2.
Все три команды в блоке, помеченном меткой Change, будут
выполнены как команды NOP (Нет операции).
3.
Нужно использовать суффикс условного выполнения «Строго
больше» GT для первого и третьего вариантов и суффикс оппозитного
условия «Меньше или равно» LE – для второго варианта.
Пример 2. В регистре r3 находится число со знаком в дополнительном коде.
Получить абсолютное значение этого числа r3 = ABS(r3).
Попытка найти команду получения абсолютного значения числа (целого или с
фиксированной точкой) в системе команд процессора не приведет к успеху. Такая
команда есть только для обработки чисел в формате с плавающей точкой VABS.F32. Как
же быть? Обратите внимание на то, что в зависимости от состояния флага N в регистре
статуса программы-приложения формируются два кода условного выполнения команд MI
113

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
(«минус» – результат отрицательный) и PL («плюс» – результат положительный).
Следовательно, можно проанализировать знак исходного операнда в регистре r3, и если он
окажется отрицательным, то получить абсолютное значение числа путем вычитания из
нуля этого числа: 0-(r3). В противном случае, значение в регистре r3 можно не менять.
Для того, чтобы установить знак исходного операнда, можно воспользоваться
разными командами. Главное, чтобы они выставляли флаги результатов операции.
Приведем несколько возможных вариантов:
; Вариант №1.
; Сравнить содержимое регистра r3 с непосредственным
; операндом нулем #0.
CMP r3, #0
;(r3)-#0. Выставить флаги
; Вариант №2.
; Сложить содержимое регистра r3 c нулем.
; Не забыть указать опцию «S» установки флагов
ADDS r3, #0
;[(r3)+#0]→ r3. Установить флаги
; Вариант №3.
; Переслать содержимое регистра r3 в свободный регистр,
; например, r0, указать опцию «S» установки флагов
MOVS r0, r3
; (r0)←(r3). Установить флаги
Как видите, вариантов много – выбирайте любой, удобный для Вас. Обратите
внимание на то, что в отличие от многих других процессоров, команда межрегистровой
пересылки данных MOV может вырабатывать флаги (если суффикс «S» указан).
Итак, флаги установлены. Прямое условие – MI («минус»), а оппозитное – PL
(«плюс»). Варианты решения:
Вариант 1:
; Если число положительное, оставить все «как есть»
BPL Next
; В противном случае - изменить знак числа на обратный
MOV r0, #0
; r0 ← #0
SUB r3, r0, r3 ; r3 ← [(r0)-(r3)]
; Здесь использована трех-операндная команда
; r3 – регистр-приемник результата операции
; r0 – 1-й регистр-источник
; r3 – 2-й регистр-источник
Next
; … Продолжение программы
Вариант 2
; Изменить знак числа на обратный, если число отрицательное
MOVMI r0, #0
; r0 ← #0
SUBMI r3, r0, r3
; r3 ← [(r0)-(r3)]
Очевидно, что и в этом примере имеет смысл использовать команды условного
выполнения вместо команд условной передачи управления. Программа получается более
наглядной и «читабельной».

114

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
Для решения подобных задач очень удобно использовать команду
так называемого реверсивного вычитания RSB {Rd}, Rn, #0 – Reverse
Subtract. Если второй операнд в команде – непосредственные данные, то
отпадает необходимость использования дополнительной команды
загрузки регистра константой (#0). При прямом вычитании из значения
первого операнда-источника вычитается значение второго операндаисточника, а при реверсивном вычитании: из значения второго операнда-источника
вычитается значение первого-операнда источника. Результат можно сохранить в любом
регистре приемнике Rd или, если он опущен, то в первом регистре-источнике.
Применительно к нашей задаче – достаточно будет всего одной команды:
; Вариант 3
; Изменить знак числа на обратный, если число отрицательное
RSBMI r3, r3, #0
; r3 ← [#0-(r3)]
1.
Мнемокод любой команды процессора формируется по одним и
тем же общим принципам. Что за корень имеет команда RSBMI? Какой у
нее префикс и что он обозначает? Какой у нее суффикс, каково его
значение?
2.
Можно ли использовать не трех- а двух-операндную команду
RSBMI r3, #0?
3. Как Вы думаете, почему команда SUBMI r3, #0, r3 является недопустимой?
1.
Корень SB – от SUB (Вычитание), сокращенный на одну букву.
Префикс R от англ. Reverse (Реверсивный порядок операции): из
вычитаемого вычитается уменьшаемое. Префикс специфицирует тип
операции, в данном случае – реверсивное вычитание. Суффикс – MI
(«Минус») – код условного выполнения команды.
2.
Да.
3. Дело в том, что только второй операнд в команде является универсальным и на его
месте можно указать не только имя регистра ЦПУ, но и непосредственный операнд –
константу (см. 7.11).

7.10 Унифицированный синтаксис команд процессора и
сопроцессора. Основные принципы кодирования команд
При создании системы команд Cortex-M специалисты фирмы ARM старались
унифицировать синтаксис команд, чтобы облегчить программистам восприятие
мнемокодов команд, их запоминание и использование. Для большинства команд,
обрабатываемых в ЦПУ и в сопроцессоре, применяется унифицированный синтаксис
команд, основанный на следующих положениях:
1. В командах обработки данных в качестве операндов могут использоваться в основном
регистры окружения ЦПУ или сопроцессора.
2. Второй
операнд-источник
является
особым:
универсальным
операндом,
допускающим, в частности, задание в качестве операндов - констант.
3. Для доступа к регистрам специального назначения (управления, статуса и т.д.)
используются только специальные команды.

115

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
4. Для доступа к памяти используются только специальные команды, имеющие особый
синтаксис кодирования адреса нужной ячейки памяти.
5. Для доступа к битам и битовым полям в регистрах ЦПУ используются специальные
команды.
6. Большинство команд выполняют операции над словами (32-разрядными операндами) с
получением 32-разрядного результата. Некоторые исключения:
a. Поддерживаются параллельные арифметические операции над одноименными
байтами, расположенными в регистрах-источниках;
b. Поддерживаются параллельные арифметические операции над полусловами,
расположенными в регистрах-источниках;
c. Поддерживаются операции над 32-разрядными словами с получением длинного
64-битного результата (умножение, умножение с накоплением).
7. В поле мнемокода команды должен быть указан основной мнемокод операции,
который может быть расширен слева опциональным префиксом и справа –
опциональным суффиксом (возможно, несколькими суффиксами). Между префиксом и
основным мнемокодом операции, между основным мнемокодом и суффиксом
разделителей нет. Полученный таким образом составной мнемокод полностью
соответствует назначению команды и операциям, ею выполняемым.
8. Префикс специфицирует конкретный тип операции или является указателем места ее
выполнения (в ЦПУ или сопроцессоре). Так, для всех команд, выполняемых в
сопроцессоре поддержки вычислений с плавающей точкой FPU, используется префикс
«V». Для команд, выполняемых в ЦПУ, он может отсутствовать или уточнять тип
операции, например, «S» (Signed) – операция с числами со знаком в дополнительном
коде; «U» (Unsigned) – операция с числами без знака.
9. Суффикс – специфицирует одну из отличительных особенностей операции.
Суффиксов может быть несколько, в соответствии с несколькими особенностями
конкретной операции. Примеры в табл. 7.6.
Таблица 7.6 Суффикс специфики операции
Суффиксы,
отражающие специфику
операции

Значение

S
8

Set – Установить флаги в регистре статуса APSR
Исходные операнды – байты (8-разрядные). Параллельное выполнение
4-байтовых операций в АЛУ
Исходные операнды – полуслова (16-разрядные). Параллельное
выполнение двух операций с полусловами.
Long – Получение длинного 64-битного результата умножения или
умножения с накоплением.
Использовать младшее полуслово в исходном 32-разрядном регистре
Использовать старшее полуслово в исходном 32-разрядном регистре
Любой из суффиксов условного выполнения команды

16
L
W
T
{cond}

10. Поле мнемокода операции отделяется от поля операндов по крайней мере одним
пробелом.
11. В поле операндов могут быть указаны в порядке слева направо: операнд-приемник
результата операции Rd или Sd (от англ. Destination – назначение), первый операндисточник Rn или Sn, второй операнд-источник Rm или Sm. Имя операнда-приемника
всегда указывается первым.
12. В качестве разделителя между операндами используется запятая.
13. В зависимости от числа операндов все команды можно разделить на четырехоперандные, трех-операндные, двух-операндные, одно-операндные и не имеющие
операндов (например, NOP).
116

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
14. В двух-операндной команде функцию регистра-приемника Rs (от англ. Source –
источник) данных выполняет первый регистр-источник Rn, имя которого ближе к
мнемокоду операции. При этом исходные данные, расположенные в нем,
автоматически замещаются результатом операции.
15. В одно-операндной команде единственный регистр, указанный в поле операндов,
выполняет функцию операнда-источника данных. Регистр-приемник не указывается, а
подразумевается «по умолчанию». Обычно это счетчик команд PC в командах
косвенной передачи управления.
16. Если в мнемокоде команды используется суффикс «L» (Long), то приемником
операции является «длинный регистр», состоящий из двух 32-разрядных регистров
общего назначения RdLo (младшее слово результата) и RdHi (старшее слово). В поле
операндов они должны быть указаны именно в таком порядке: RdLo, RdHi. В этом
случае поле операндов должно содержать имена 4-х регистров.
17. В командах умножения с накоплением в поле операндов может присутствовать четыре
регистра: регистр-приемник Rd, регистр-аккумулятор Ra, регистр первого исходного
операнда Rn (множителя), регистр второго операнда Rm (множителя). В операциях
умножения с накоплением, кроме самой последней операции, имена регистров Rd и Ra
должны совпадать.
Четырех-операндные команды используются в операциях цифровой обработки
сигналов (DSP). Их назначение и синтаксис подробно рассматриваются в
соответствующих главах книги. Представим здесь только синтаксис большинства,
наиболее часто применяемых команд.

7.10.1 Синтаксис трех-операндных команд
Трех-операндная команда
Поле мнемокода команды


Основной
мнемокод
операции

Поле операндов


˽

Имя
регистраприемника
Rd или Sd

,

Имя
1-го , Имя
2-го
регистрарегистраисточника Rn
источника Rm
или Sn
или Sm

Примеры:
ADD r0, r3, r4
ADDS r0, r3, r4
SMULBT r0, r3, r4

;
;
;
;
;
;
;
;
;
;
;

Сложить содержимое регистров r3 и r4.
Сохранить сумму в регистре r0.
То же самое, но, дополнительно выставить
флаги результата операции в регистре
статуса приложения APSR.
Выполнить знаковое умножение –
префикс «S» (Signed) младшего (B - Bottom)
полуслова первого операнда в r3
на старшее полуслово (T - Top) второго
операнда в r4 и сохранить результат в
регистре r0.

VMUL.F32 s0, s3, s4
; Выполнить умножение в модуле плавающей точки FPU
; (префикс V) чисел с плавающей точкой однократной точности
; (суффикс «.F32»)в регистрах источниках s3, s4
; с сохранением произведения в регистре s0.

117

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
Как видите, корневая часть мнемокода операции определяет ее основное
содержание: ADD – от англ. addition – сложение, MUL – от англ. multiplication –
умножение. Префикс определяет главную специфику команды. Так, в третьем примере он
указывает на то, что операция умножения будет знаковой – над операндами со знаком в
дополнительном коде. В последнем примере указывает на то, что операция будет
выполнена в сопроцессоре над операндами в формате с плавающей точкой. Для
большинства команд, выполняемых в операционном блоке ЦПУ, префикс отсутствует, как
в первых двух командах.
Суффикс подчеркивает особенности в выполнении команды. Один из важнейших
суффиксов «S» (вторая команда) заставляет процессор выставить по результату текущей
операции флаги результата операции и сохранить их в регистре статуса приложения
APSR. Суффикс «.F32» в последней команде специфицирует формат чисел с плавающей
точкой, которые будут обрабатываться – 32-разрядные числа однократной точности. Хотя
сопроцессор Cortex-M4F и не поддерживает арифметические операции над числами с
плавающей точкой двойной точности (64-разрядными), он поддерживает операции
загрузки и сохранения таких чисел. Поэтому суффикс «.F32» указывается для
определенности.
Приведем еще один пример:
SMULL r0, r1, r7, r8
Это четырех-операндная команда по синтаксису (в поле операндов перечислены
имена четырех регистров) по сути является трех-операндной, в которой содержимое
первого регистра-источника r7 умножается (корень мнемокода MUL) на содержимое
второго регистра-источника r8 и длинное (суффикс L –Long) 64-битное произведение
сохраняется в паре регистров r0, r1 (в r0 – младшее слово RdLo, в r1 – старшее RdHi).
Префикс «S» свидетельствует о том, что операция умножения выполняется над числами
со знаком в дополнительном коде.

7.10.2 Синтаксис двух-операндных команд
Двух-операндная команда
Поле мнемокода команды


Основной
мнемокод
операции

Поле операндов


˽

Имя 1-го
регистраисточника
и одновременно
регистраприемника
Rn или Sn

,

Имя 2-го
регистраисточника Rm
или Sm

118

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
Примеры:
SUB r3, r4
SUBS r3, r4
UDIV r5, r6

VDIV.F32 s1, s2

; Вычесть из содержимого регистра r3
; содержимое регистра r4.
; Сохранить результат в регистре r3.
; То же, но по результату операции вычитания
; выставить флаги в регистре статуса APSR.
; Разделить 32-разрядное число без знака,
; префикс «U» (Unsigned), в регистре r5 на
; 32-разрядне число без знака в регистре r6.
; Сохранить частное в регистре r5.
; Разделить число с плавающей точкой
;(префикс «V»), однократной точности
;(суффикс «.F32»)в регистре s1 на число
; с плавающей точкой в регистре s2.
; Сохранить частное в регистре s1.

В этих примерах корневая часть мнемокода операции строго соответствует ее сути:
SUB – от англ. Subtraction – Вычитание, DIV – от англ. Divide – Деление. Значения
префиксов и суффиксов отражены в комментариях к командам. Обратите внимание на то,
что во всех двух-операндных командах первый регистр-источник одновременно является
и регистром-приемником данных. Следовательно, с одной стороны – эти команды могут
быть более короткими (более компактными), чем трех-операндные (входить в набор 16разрядных команд Thumb), а с другой – будут замещать («затирать») данные в первом
регистре-источнике. Если исходные данные больше не нужны – это не критично. В
противном случае лучше использовать трех-операндную команду, которая и сохранит
исходные данные и направит результат операции по месту назначения.
1)
2)
3)

Как Вы думаете, допустима ли такая команда:
ADD r1, r1, r2?
А такая:
ADD r1, r1, r1?
Почему последнюю команду лучше заменить на команду:
ADD r1, r1?

1)
Да. Никаких ограничений на использование регистров ЦПУ или
сопроцессора в поле операндов нет.
Эта команда выполнит операцию [(r1)+(r2)] → r1.
2)
Тоже допустима. Это просто удвоение содержимого регистра r1.
3)
Выполняет те же действия, что и предыдущая команда. Более
проста по синтаксису.
Транслятор с Ассемблера, являясь«интеллектуальным», всегда
пытается сгенерировать более компактный код (16-разрядного набора
Thumb), если это возможно. Не удивляйтесь, если вместо написанной Вами
трех-операдной команды Вы увидите полностью соответствующую ей двухоперандную. Если все три регистра в поле операндов разные, то транслятор
сгенерирует нужную Вам трех-операндную команду.

119

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M

7.10.3 Синтаксис одно-операндных команд
Одно-операндная команда
Поле мнемокода команды


Основной
мнемокод
операции

Поле операндов


˽

Имя
регистраисточника
Rm

Примеры:
BX

Rm

BLX Rm

;
;
;
;
;

Косвенный переход по адресу в регистре Rm
(Rm)→ PC
Косвенный переход по адресу в регистре Rm
с предварительным сохранением адреса возврата
в регистре связи LR

Представленные выше команды являются командами этого класса условно. Ведь
на самом деле регистр приемник в них присутствует, но «по умолчанию» – это счетчик
команд PC.
Итак, большая часть команд процессора двух- и трех-операндные.
Даже такие команды, как получение абсолютного значения числа в формате
с плавающей точкой или вычисление квадратного корня из него формально
двух-операндные: результат операции над одним операндом в регистреисточнике можно сохранить либо в том же самом регистре, либо в любом
другом Sd:
VABS.F32 Sd, Sm
VSQRT.F32 Sd, Sm

; |(Sm)|→ Sd
; [√(Sm)] → Sd

Имеется и небольшая группа команд, не имеющих никаких операндов. Это
специальные операции, одна из которых – команда NOP (No Operation – Нет операции).
Процессоры Cortex-M имеют разветвленную систему команд, которая, с одной
стороны, достаточно сложна для изучения, а с другой – настолько мощная, что позволяет
решать практически любые задачи обработки данных, причем в любых форматах (бита,
байта, полуслова, слова). Совет: старайтесь понять логику разработчиков системы
команд, используемую при их кодировании. Тогда система команд не покажется
«чрезмерно сложной», а наоборот – логически стройной и понятной. Мы постараемся
помочь Вам в этом.

7.11 Гибкий второй операнд-источник. Попутные операции
в сдвиговом регистре
7.11.1 Гибкий второй операнд
Для ряда арифметических, логических команд и команд пересылки, которые
выполняются с использованием АЛУ процессора, возможности второго операнда,
поступающего во второй порт АЛУ, существенно расширены. Если первый операнд
может извлекаться только из регистра общего назначения, то второй – из трех возможных
источников:

120

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
1) Из кода операции, когда непосредственный операнд #Const содержится в формате
самой команды;
2) Из регистра общего назначения ЦПУ без какой-либо обработки – Rm;
3) Из регистра общего назначения ЦПУ c предварительной обработкой в специальном
сдвиговом регистре процессора – Rm, shift.
Такой операнд, в отличие от обычного операнда-регистра, имеет в системе команд
специальное обозначение Op2 и называется «гибким вторым операндом-источником»
(Flexible Second Operand). Синтаксис команд процессора с гибким вторым операндом
продемонстрируем на примере команды сложения:
ADD {Rd}, Rn, Op2
По-прежнему, сразу после мнемокода операции опционально (не обязательно)
указывается имя регистра-приемника Rd (заключено в фигурные скобки), затем имя
первого регистра-источника Rn, а после него – имя второго гибкого операнда Op2. При
использовании во втором операнде содержимого регистра Rm, синтаксис Op2 выглядит
так: Rm {,shift}. Фигурные скобки указывают на то, что предварительный сдвиг
содержимого регистра Rm – опция. Вы можете выполнить предварительную попутную
обработку содержимого регистра Rm с использованием специальной операции в
сдвиговом регистре shift (Сдвиг), либо отказаться от нее. В этом случае данные из второго
регистра-источника Rm будут поступать в порт АЛУ напрямую, минуя сдвиговый
регистр, в обход него, через байпас – (bypass).

7.11.2 Второй операнд – константа
Для того, чтобы в качестве второго операнда команды задать константу,
необходимо в программе на языке Ассемблер указать ее числовое значение после символа
«#», являющегося признаком непосредственного операнда, то есть операнда, значение
которого будет автоматически помещено транслятором с Ассемблера в формат команды и
будет доступно процессору на стадии выполнения по содержимому кода команды.
Теоретически Вы можете задать любую 32-разрядную константу. Практически это
невозможно, так как в этом случае для ее представления пришлось бы выделить отдельное
32-разрядное слово в команде и формат команды превысил бы 32 бита:
Теоретически возможный формат команды с непосредственной адресацией
Первое слово
Второе слово

Код операции
32-разрядные данные (константа)

Разработчики системы команд пошли по другому пути: они ограничились
некоторыми, наиболее употребительными на практике константами, упаковав
(зашифровав) их так, чтобы они могли разместиться в 32-разрядном формате команды
вместе с другими полями, в том числе, с полем кода операции.
Мы не будем подробно изучать механизмы кодирования
непосредственных данных в процессорах ARM, которые могут отличаться от
команды к команде. Для любознательных читателей рассмотрим два наиболее
часто используемых варианта компрессии:
1) Задается любая 8-разрядная константа (от 0 до 255) и число бит сдвига этой константы
влево для размещения внутри 32-разрядного слова. Максимальное число разрядов
сдвига для размещения байта в старших разрядах слова – 24. Для его представления
достаточно пяти бит. Следовательно, вместе с байтовой константой потребуется
выделить в формате команды всего 8+5=13 бит.

121

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
2) Задаются две 4-битовые константы X и Y (X и Y – любые шестнадцатеричные цифры),
для хранения которых достаточно одного байта и двухбитовый признак типа
кодирования. В этом случае в формате команды для представления константы
потребуется только 8+2=10 бит:
Зашифрованная константа
4 бита: 0xX
4 бита: 0xY
Тип кодирования (2 бита):
0
0
1

0
1
0

Константа
(Расшифрованное значение, поступающее на обработку в
АЛУ в качестве операнда)
0x00XY00XY
0xXY00XY00
0xXYXYXYXY

В некоторых командах диапазон представления констант шире (см. приложение 1).
Для обычного пользователя нужно знать следующее:
• Транслятор с Ассемблера автоматически выбирает оптимальный способ кодирования
указанной Вами константы;
• Если размещение константы в формате самой команды невозможно, транслятор
сгенерирует соответствующее предупреждение. Что делать в этом случае? Вариантов
несколько: 1) попробовать задать близкую по значению, но другую константу; 2)
разместить константу в регистре общего назначения Rm и в качестве гибкого операнда
Op2 использовать регистр Rm с дополнительным сдвигом; 3) воспользоваться для
загрузки константы (любой 32-разрядной) специальной псевдокомандой Ассемблера,
которая предварительно размещает константу в ПЗУ в виде так называемого
«литерального пула», а затем обеспечивает ее загрузку с использованием адресации по
содержимому счетчика команд (относительной адресации) – подробно в 9.1.
Транслятор
с
языка
Ассемблера
является
высоко
интеллектуальным продуктом, который за Вас может автоматически
принять более эффективное решение. Это справедливо и в том случае,
если Вы задали константу в неудачном формате, например, записали
команду сравнения с константой в виде:
CMP Rn, #0xFFFFFFFE ; Сравнить содержимое регистра Rn с (-2)
Транслятор автоматически заменит эту команду командой сравнения
отрицательным значением операнда (Compare negative):
CMN Rn, #0x2
; После отрицания получим те же (-2)

с

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

122

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M

7.11.3 Аппаратная поддержка операций предварительной обработки данных
в сдвиговом регистре
Выделим из программной модели процессора, изображенной на
рис. 7.1, часть, касающуюся обработки данных в АЛУ – рис. 7.6. Одним из
операндов обязательно будет регистр общего назначения процессора.
Второй (универсальный операнд) Op2 может быть: 1) константой
(непосредственным операндом), считываемой из регистра команд; 2)
регистром общего назначения; 3) регистром общего назначения, содержимое
которого проходит предварительную обработку в сдвиговом регистре.
Регистр команд

Такая архитектура операционного блока
ЦПУ характерна для высокопроизводительных
r0
r1
сигнальных процессоров, которые обрабатывают
r2
числа в формате с фиксированной точкой.
r12
Действительно, если один из операндов
r13-SP
r14-LR
арифметической операции имеет формат (m.n), а
r15-PC
второй формат (i.q), причем числа разрядов
дробной части n и q в них разные, то приходится
2
3
1
приводить операнды к одинаковому формату,
Сдвиговый
сдвигая один из них влево или вправо так, чтобы
регистр
двоичные точки в операндах располагались
одинаково.
Только
в
этом
случае
арифметические операции над числами с
АЛУ
фиксированной точкой будут корректными.
Имея в архитектуре процессора кольцевой
сдвиговый регистр, операцию приведения чисел
к одинаковому формату можно сделать
попутной, выполняемой еще до загрузки
Рис. 7.6 Аппаратная поддержка
операнда
в порт АЛУ. Это дает существенный
попутной обработки операнда в
выигрыш в производительности процессора. В
кольцевом сдвиговом регистре
Сortex-M попутные операции не увеличивают
время выполнения основной арифметической или логической операции, которая, как и
подавляющее большинство операций в RISC-процессорах, выполняется всего за один цик.
Опция попутной обработки позволяет также масштабировать один из операндов,
участвующих в операции, на коэффициент 2n, где n – число разрядов сдвига (имеется в
виду арифметический сдвиг, ниже - подробнее).
Регистры
общего назначения
r0÷ r15

7.11.4 Типы попутных операций
В таблице 7.7 приведены возможные варианты попутных операций сдвига (shift),
которые могут быть выполнены с данными, расположенными в регистре-источнике Rm:
Таблица 7.7 Варианты попутных операций сдвига
Shift

Выполняемые действия

ASR #n
LSR #n
LSL #n
ROR #n
RRX


Арифметический сдвиг вправо на n бит, 1 ≤ n ≤ 32
Логический сдвиг вправо на n бит, 1 ≤ n ≤ 32
Логический сдвиг влево на n бит, 1 ≤ n ≤ 31
Циклический сдвиг вправо на n бит, 1 ≤ n ≤ 31
Циклический сдвиг вправо на один бит с расширением, – через флаг C (Carry)
Если опция опущена, то сдвиг не выполняется. Эквивалентно LSL #0

123

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
Графическая иллюстрация возможных вариантов сдвига представлена на рис. 7.7.
Рассмотрим каждый из типов сдвига более подробно:
S

ASR #3

S

31 30 29 28 27 26 25

0

LSR #3

S

0

5

4

3

2

1

0

C

5

4

3

2

1

0

C

0

0

0

0

31 30 29 28 27 26 25

LSL #3

C 31 30 29 28 27 26 25

5

4

3

2

1

0

ROR #1

C 31 30 29 28 27 26 25

5

4

3

2

1

0

ROR #3

C 31 30 29 28 27 26 25

5

4

3

2

1

0

RRX

C 31 30 29 28 27 26 25

5

4

3

2

1

0

Рис. 7.7 Варианты попутного сдвига второго операнда Op2
7.11.4.1 Арифметический сдвиг вправо – ASR #n

Арифметический сдвиг вправо (Arithmetic Shift Right – ASR) на n бит перемещает
все 32 бита исходного операнда в регистре Rm вправо на n бит. При этом вытесняемые
вправо за пределы разрядной сетки биты сначала попадают в битовый флаг C (Carry), а
при дальнейшем сдвиге теряются. Дополнительно выполняется копирование исходного
31-го бита операнда (знакового разряда числа в дополнительном коде) в n старших бит
результата. Операция «автоматического размножения» знакового разряда исходного
числа выделена на рис. 7.7 красным цветом. Такой сдвиг эквивалентен последовательному
делению знакового операнда на 2 при каждом очередном сдвиге, отсюда и название
операции – «арифметический сдвиг вправо».
Если сдвиг выполняется на n разрядов, то исходное число со знаком в регистре Rm
делится на 2n. Если попутная операция используется для «выравнивания» положения
двоичной точки в вещественном числе, то самые младшие разряды (обычно разряды
дробной части числа) теряются. При операциях с вещественными числами – это, обычно,
незначимые разряды.
Если операция, в которой используется предварительный сдвиг второго операнда,
содержит в мнемокоде суффикс «S» (Set – установить флаги), то флаг переполнения С
124

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
(Carry) в регистре статуса APSR будет обновляться последним вытесненным за пределы
разрядной сетки битом исходного регистра bit[n-1]. Например, при n=1 – нулевым битом.
В противном случае, флаг С не будет обновляться. Если выполняемая операция сама
меняет содержимое флага C (например, в результате переполнения), то флаг C
установится в соответствие с результатом основной операцией.
Примеры:
MOV r1, r2, ASR #3

; Загрузить в регистр r1 содержимое
; регистра r2, предварительно сдвинутое
; арифметически на 3 разряда вправо
;(знаковое деление на 23=8)
ADDS r0, r1, r2, ASR #1 ; Предварительно разделить операнд
; в регистре r2 на два и сложить
; с операндом в регистре r1.
; Сохранить сумму в регистре r0
; Установить флаги результата
; Арифметической операции
Каждой операции попутного сдвига, которая рассматривается в этой
главе, соответствует аналогичная логическая операция сдвига содержимого
регистра-источника на заданное число разрядов (глава 11). Их проще
использовать, если необходим последовательный анализ битовых
переменных в исходном регистре по состоянию флага переноса С. Коды
условного выполнения: CS – «флаг С установлен», CC – «флаг C сброшен»).
7.11.4.2 Логический сдвиг вправо LSR #n
Операция логического сдвига вправо (Logical Shift Right –LSR) отличается от
операции арифметического сдвига вправо тем, что вместо «авто-размножения знакового
разряда числа» выполняется операция авто-заполнения освобождающихся при сдвиге
старших разрядов нулями (выделена красным цветом на рис. 7.7). При этом все разряды
исходного числа по-прежнему сдвигаются вправо на заданное число разрядов.
Вытесняемые вправо разряды сначала попадают во флаг переноса, а при дальнейшем
сдвиге – теряются.
Операцию логического сдвига вправо часто используют для деления числа без
знака (целого или вещественного) на 2n. При этом младшие разряды частного, не
помещающиеся в 32-разрядную сетку АЛУ, отбрасываются.
Если в команде, использующей попутную операцию LSR #n, имеется суффикс
формирования флагов результатов операции «S» (Set), то флаг C (Carry) окажется
обновлен последним, вытесненным вправо битом bit[n-1]. Например, при сдвиге на n=3
разряда, битом bit[2], что и показано на рис. 7.7. Если основная команда сама воздействует
на флаг C, то его содержимое будет определяться результатом выполнения основной
команды.
7.11.4.3 Логический сдвиг влево LSL #n
Логический сдвиг влево (Logical Shift Left - LSL) на заданное число разрядов n
перемещает содержимое исходного регистра Rm влево на требуемое число разрядов с
автоматическим заполнением освобождающихся справа разрядов нулями 0 и
«вытеснением» старших разрядов во флаг переноса C с последующей их потерей при
дальнейшем сдвиге.
Эта операция эквивалентна умножению исходного числа без знака на 2n, при
условии, что переполнения не возникло. Если в результате такой операции переполнение
125

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
имеет место (теряются один или более ненулевых старших разрядов, вытесненных за
пределы разрядной сетки), то (внимание!) – нет флага, свидетельствующего об этом.
Если в команде, использующей попутную операцию LSL #n, имеется суффикс
установки флагов результатов операции «S» (Set), то флаг C (Carry) окажется обновлен
последним, вытесненным влево битом bit[32-n]. Например, при сдвиге на n=3 разряда, –
битом bit[29], что и показано на рис. 7.7. Если основная операция работает с флагом C, то
она и определяет состояние этого флага.
7.11.4.4 Циклический сдвиг вправо ROR #n
Циклический сдвиг вправо (ROtate Right – ROR) на заданное число разрядов n
выполняется над содержимым регистра Rm так, как будто бы это кольцевой сдвиговый 32разрядный регистр. Вытесняемый за пределы разрядной сетки вправо разряд попадает в
самый старший разряд регистра и одновременно во флаг переноса С.
На рис. 7.7 эта операция вначале иллюстрируется сдвигом только на один разряд
ROR #1, а затем на 3 разряда ROR #3. Если в команде, использующей попутную операцию
ROR #n, имеется суффикс установки флагов результатов операции «S» (Set), то флаг C
(Carry) окажется обновлен последним, вытесненным вправо битом bit[n-1]. В первом
случае – битом bit[0], а во втором – битом bit[2]. Если основная операция воздействует на
флаг C, то она и определяет состояние этого флага.
7.11.4.5 Циклический сдвиг вправо c расширением RRX
Циклический сдвиг вправо с расширением (Rotate Right with eXtend- RRX)
представляет собой циклический сдвиг вправо на один разряд в 33-разрядном регистре,
расширенном на один разряд флагом переноса. Регистр Rm и флаг переноса C (Carry)
рассматриваются как один 33-разрядный кольцевой сдвиговый регистр.
Все разряды такого расширенного регистра сдвигаются вправо на один разряд, в
результате чего старший разряд обновляется содержимым флага С, а флаг С –
содержимым младшего бита исходного операнда bit[0]. Он установится в регистре статуса
APSR только в том случае, если команда, использующая попутную операцию RRX, имеет
суффикс «S» (Set) в своем мнемокоде.
Операция RRX используется обычно для сдвига вправо на нужное число разрядов
длинных чисел (64-разрядных и более). При этом «выдвинутый» из старшего слова
младший бит сначала попадает в флаг C, а при втором сдвиге автоматически «вдвигается»
в младшее слово.
1)
Чему должно быть равно n в операции попутного арифметического
сдвига вправо ASR #n, чтобы все биты исходного операнда заполнились
знаковым разрядом числа?
2)
При каком значении n в операции попутного логического сдвига
вправо LSR #n все биты исходного операнда окажутся обнуленными?
3)
В регистре Rm находится число 232=0x80000000. Выполнятся
операция попутного логического сдвига влево на число разрядов n≥1. Чему окажется
равен операнд, загружаемый во второй порт АЛУ?
4) В регистре r3 находится битовая маска, в которой все разряды младшего байта
установлены в 1 – 0x000000FF. Требуется установить в 1 все разряды старшего байта
числа в регистре r2 c помощью операции «Логического ИЛИ» (ORR) со вторым
гибким операндом, попутно преобразующим исходную маску в маску 0xFF000000.
5) Маска в регистре r3 содержит нули во всех 16-и младших разрядах (в младшем
полуслове) – 0xFFFF0000. Какую попутную операцию нужно выполнить, чтобы
получить маску с нулями в старшем полуслове 0x0000FFFF? Как обнулить с ее

126

ГЛАВА 7. ПРОГРАММНАЯ МОДЕЛЬ ПРОЦЕССОРОВ CORTEX-M
помощью старшее слово регистра r2, используя команду «Логического И» (AND)
содержимого r2 c маской?
1)
n ≥ 31.
2)
При n=32.
3)
Нулю. Более того, никакого флага о возникшем переполнении
(выходе за пределы разрядной сетки) сформировано не будет.
4)
Нужная операция преобразования маски может быть выполнена с
использованием циклического сдвига вправо на 8 разрядов: ROR #8. Если
результат операции «маскирования» должен быть сохранен в том же регистре r2, то
соответствующая команда будет двух-операндной, в которой первый регистр-источник
будет и регистром-приемником:
ORR r2, r3, ROR #8

; Логическое побитовое ИЛИ: (r2)+ 0xFF000000

5) Достаточно выполнить попутный циклический сдвиг на 16 разрядов ROR #16, чтобы
исходная маска с нулями в младшем полуслове стала маской с нулями в старшем
полуслове: 0xFFFF0000 → 0x0000FFFF. Операция побитового логического умножения
на такую маску очистит все разряды старшего полуслова в регистре r2.
AND r2, r3, ROR #16 ; Логическое побитовое И: (r2)∙(0x0000FFFF)
В данном примере вместо попутной операции циклического сдвига вправо можно
использовать и операцию логического сдвига влево на те же 16 разрядов LSR #16.

Список рекомендуемой литературы
1) Джозеф Ю. Ядро Cortex-M3 компании ARM. Полное руководство/ Ю. Джозеф; пер.
с англ. А.В. Евстафеева. – М.: Додэка-XXI, 2012. – 552 c. ил. (Мировая
электроника).
2) Козаченко В.Ф. Микроконтроллеры: руководство по применению 16-разрядных
микроконтроллеров Intel MCS-196/296 во встроенных системах управления. – М.:
ЭКОМ, 1997. – 688 с.
3) ARM DDI 0403C_errata_v3, ARMv7-M Architecture Reference Manual, Arm Limited,
2010.
4) Texas Instruments. SPMU 159a. Cortex-M3/M4F Instruction Set. Technical User’s
Manual. Texas Instruments Inc. – 2011.
5) Mahout V. Assembly language programming: ARM Cortex-M3. – Wiley-ISTE. – 2012.
6) Hohl W., Hinds C. ARM Assembly language. Fundamentals and Techniques. Second
Edition. – N.-Y.: CRC Press.– 2015. – 453 p.

127

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ

8 ТЕХНОЛОГИЯ РАЗРАБОТКИ
ПРОГРАММ С
ИСПОЛЬЗОВАНИЕМ КРОСССРЕДСТВ
Оглавление
8.1. Общие положения .............................................................................................................. 129
8.2. Интегрированная среда разработки Keil μVision ............................................................ 130
8.3 Поддерживаемые языки программирования .................................................................... 131
8.4. Состав пакета Keil μVision. Этапы разработки ПО ......................................................... 132
8.4.1. Компоненты интегрированной среды Keil μVision ................................................. 132
8.4.2. Основные этапы разработки и отладки программного обеспечения .................... 133
8.5. Окно интегрированной среды μVision5 ........................................................................... 136
8.6. Назначение псевдокоманд – директив Ассемблера ........................................................ 138
8.7. Использование символических имен в программе ......................................................... 138
8.8. Секционирование программных модулей....................................................................... 140
8.8.1. Директива объявления секции .................................................................................. 140
8.8.2. Директива DCI размещения констант в кодовой секции ....................................... 143
8.8.3. Директива резервирования переменных в секциях данных SPACE ..................... 144
8.8.4. Директива заполнения области памяти константой FILL ...................................... 145
8.8.5. Директивы размещения в памяти констант в заданном формате .......................... 146
8.9. Автоматическое вычисление выражений транслятором ................................................ 147
8.10. Внутренняя и внешняя модульность программы .......................................................... 149
8.10.1. Внутренняя модульность ......................................................................................... 149
8.10.2. Директивы объявления функций PROC и FUNCTION ........................................ 149
8.10.3. Внешняя модульность .............................................................................................. 150
8.10.3.1. Директива объявления глобального (общедоступного) имени ................... 150
8.10.3.2. Директива объявления внешнего имени ........................................................ 151
8.10.4. Реализация внешней модульности в программах ............................................ 152

128

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ

8.1 Общие положения
Программное обеспечение микропроцессорных систем сегодня
разрабатывается исключительно на персональных компьютерах с
использованием кросс-средств, то есть таких программных продуктов,
которые устанавливаются и работают на универсальных компьютерах,
создавая исполняемые файлы, предназначенные для загрузки и
выполнения в конкретном процессоре или микроконтроллере (целевом
устройстве), используемом разработчиком в своей системе.
Современные кросс-средства являются интегрированными системами, которые
включают в себя все необходимые пользователю инструменты, объединенные общей
графической средой, позволяющей практически полностью совместить процесс
разработки ПО с процессом отладки. При этом пользователю предоставляются несколько
возможностей отладки: 1) в симуляторах – программно-логических моделях целевого
процессора, работающих исключительно на ПК и не требующих какой-либо
дополнительной аппаратуры; 2) в созданных производителями микропроцессоров и
микроконтроллеров оценочных платах, подключенных к ПК по универсальному
интерфейсу USB; 3) в конечных устройствах, аппаратная часть которых создана
разработчиками конкретных приложений (контроллерах) и имеющих необходимые
интерфейсы для подключения отладчиков (обычно JTAG). При этом обеспечивается
единый интерфейс программы отладчика с любым из трех устройств, в котором
выполняется программа, и унификация функций отладки – облегчается взаимодействие
специалистов-аппаратчиков и программистов при создании любых микропроцессорных
систем.
Современные интегрированные среды поддерживают разработку как минимум на
Ассемблере и одном из языков высокого уровня (обычно С/С++). При этом допускается
комбинированная разработка, когда часть программных модулей создается на языке
высокого уровня, а часть – на Ассемблере. Допускаются и ассемблерные вставки в
программы, написанные на языке высокого уровня. Интересно, что по мере развития
микропроцессорной техники произошло обогащение языка Ассемблер средствами,
которые раньше применялись исключительно в языках высокого уровня. Это касается как
команд собственно процессора (например, условного выполнения операций), так и
специальных директив Ассемблера, псевдокоманд, которые интегрируют в Ассемблер
целый рад инструментов, присущих ранее только языкам высокого уровня. Таким
образом, синтаксис современного языка Ассемблер существенно расширен. Это не только
синтаксис команд процессора, но и ряд вспомогательных операций – директив
Ассемблера, которые позволяют не только создавать исполнительный код программы, но
и секционировать программу, управлять размещением переменных и констант в памяти,
выполнять удобную символьную отладку программного продукта.
В этой главе мы выделим важнейшие принципы, которым по умолчанию следуют
все разработчики программных продуктов и которые сегодня стали «стандартом дефакто». Основное внимание уделим технологии разработки ПО на Ассемблере. В
последующих главах книги используем эти знания для практического изучения
эффективных способов решения типовых задач управления на основе системы команд
процессоров Cortex-M3/M4/M4F, попутно изучая ее возможности. В качестве основы для
дальнейшего изложения выберем интегрированную среду разработки Keil μVision.

129

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ

8.2 Интегрированная среда разработки Keil μVision
В этой книге будет использоваться одна из наиболее распространенных
интегрированных сред разработки и отладки программного обеспечения для процессоров
ARM (ARM Development Kit) – Keil μVision IDE. Причины выбора этой среды следующие:
1) Полный набор инструментов (специальных программ), интегрированных в единую
графическую среду и предназначенных для создания и отладки программного
обеспечения широкого класса микропроцессорных устройств на базе процессоров
фирмы ARM, в том числе на базе процессорных ядер Cortex-M3/M4/M4F.
2) Наличие общедоступной версии (упрощенной) с некоторыми ограничениями по
функциональности, предназначенной для работы на любых компьютерах под
операционной системой Windows.
3) Ограничение состоит только в том, что проекты, генерирующие код объемом больше
чем 32 К байта нельзя скомпилировать и скомпоновать. Ограничение не является
сколько-нибудь существенным для студентов, изучающих микропроцессорную
технику, и специалистов, повышающих свою квалификацию. Объема кода 32 К байта
достаточно для отладки даже весьма сложных проектов.
4) Ядро среды содержит все средства разработки (development tools) и дополнительные
пакеты программного обеспечения (software packs) в стандарте программного
интерфейса для микроконтроллеров ARM-Cortex (Cortex Microcontroller Software
Interface standard -CMSIS), включая промежуточные библиотеки для поддержки
микроконтроллеров
большинства
фирм-производителей,
которые
могут
использоваться в качестве «целевых» устройств (target devices), то есть устройств, на
которых планируется прикладная разработка.
5) Фирма Keil давно специализируется на разработке программного обеспечения для
процессоров ARM, а в 2005 г. была куплена фирмой ARM и стала по сути ее
подразделением. Это гарантия того, что в программном обеспечении фирмы Keil
тщательно учтены все имеющиеся аппаратные и программные возможности ARMпроцессоров.
6) Среда содержит встроенный симулятор, максимально полно отражающий особенности
архитектуры и системы команд процессорных ядер ARM, удобный как для новичков,
так и для специалистов в области микропроцессорной техники.
Для того, чтобы скачать открытую среду разработки через Internet, нужно зайти на
сайт фирмы ARM. Фирма Keil является ее подразделением (http://www.keil.com/company/).
Перейти
в
раздел
продуктов
MDK
Microcontroller
Development
Kit»
(http://www2.keil.com/mdk5). Зарегистрироваться. После регистрации инсталляция
продукта выполняется автоматически.
В конце инсталляции могут устанавливаться библиотеки для конкретного
устройства (микроконтроллера одного из известных производителей). Вы можете указать
тип этого микроконтроллера или название оценочной платы, используемой в Вашей
разработке. Для них могут быть установлены также примеры практического
использования.
После того, как Вы установите комплект разработчика μVision5 (на момент
написания книги версия 5 – последняя) на рабочем столе компьютера появится
пиктограмма пакета, позволяющая быстро вызывать его с рабочего стола компьютера.
На панели инструментов пакета имеется значок установщика, который позволяет,
при необходимости, обновить пакет до последней версии.
130

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
В среде μVision имеется встроенное руководство пользователя, доступ к которому
возможен через пункт меню Help – Справка. Мы будем раскрывать богатые возможности
среды μVision по мере необходимости. Каждый раз соответствующую информацию будет
предварять приведенная выше пиктограмма пакета.
Встроенная справочная система обширна и позволяет быстро найти практически
любую нужную информацию, в том числе по любой команде процессора (для этого,
правда, нужно знать мнемокод команды) или директиве Ассемблера. Информация
выдается на языке разработчика – английском.

8.3 Поддерживаемые языки программирования
Интегрированная среда разработки Keil μVision предназначена для создания и
отладки программного обеспечения на языке Ассемблер фирмы ARM, имеющем название
ARM ASM, а также на языках высокого уровня С или С++ (далее С/С++), наиболее
востребованных прикладными программистами.
Преимущество использования языка Ассемблер фирмы разработчика процессоров
очевидно: он максимально полно отражает все особенности архитектуры процессоров
ARM и их набора команд. Относительно выбора в качестве языка высокого уровня, языка
C/C++, можно сказать следующее: на сегодня это самый распространенный язык высокого
уровня, используемый большинством высококвалифицированных программистов во всем
мире, особенно теми, которым часто приходится программировать на «низком уровне»,
управляя большим количеством встроенных в микроконтроллеры периферийных
устройств. Пожалуй, это самый удобный язык высокого уровня, создающий предельно
компактный и быстро исполняемый код, который может конкурировать с кодом,
созданным на Ассемблере. Он отличается универсальностью, удобством доступа к памяти
и регистрам периферийных устройств. Именно поэтому он широко используется при
разработке ПО встраиваемых в оборудование цифровых систем управления.
Особенность любых интегрированных сред разработки – возможность создания
программного обеспечения из модулей, написанных как на языке высокого уровня С/С++,
так и на Ассемблере. При этом Ассемблер используется там, где нужна предельная
оптимизация кода по объему памяти и быстродействию. Его можно использовать также
для создания эффективных вставок в программах на языке С/С++.
Среда μVision поддерживает не только написание кода программы на двух языках,
но и отладку программы в исходных кодах (как на С/С++, так и на Ассемблере).
В книге мы будем изучать систему команд процессоров Cortex-M и приемы
эффективного решения типовых задач управления оборудованием на Ассемблере. Такие
возможности языка ARM ASM, как директивы секционирования программ,
резервирования памяти, инициализации констант, псевдокоманды Ассемблера,
упрощающие и облегчающие программирование, макросредства и др., будут рассмотрены
по ходу изложения основного материала. Эти сведения всегда будет предварять
пиктограмма Ассемблера фирмы ARM.
Специальных глав по языку Ассемблера в книге нет.

131

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ

8.4 Состав пакета Keil μVision. Этапы разработки ПО
8.4.1 Компоненты интегрированной среды Keil μVision
Интегрированная среда разработки имеет в своем составе все инструменты,
необходимые для эффективной работы программиста:
1) Текстовый редактор с полными возможностями качественного ввода и
редактирования текста программы и дополнительными возможностями цветового
выделения синтаксических конструкций языков Ассемблер (мнемокодов команд,
директив Ассемблера, регистров и т.д.) и С/С++, что обеспечивает более удобное
восприятия текста программы как в процессе ее редактирования, так и в процессе
отладки. Конечно, для создания программ можно использовать и любые другие,
удобные для программиста текстовые редакторы, главное – они не должны включать в
текст программы специальные символы форматирования. Эти символы не
распознаются транслятором.
2) Транслятор с языка Ассемблер, воспринимающий на входе файл на Ассемблере с
расширением «.s» или «.asm» и генерирующий выходной файл в перемещаемом
объектном коде с расширением «.o» (или «.obj»), а также файл листинга с
расширением «.lst», содержащий результаты трансляции, в том числе, с сообщениями
об ошибках. Дополнительно в файл листинга может включаться таблица символов,
используемых в программе.
3) Компилятор с языка С/С++, воспринимающий на входе файл на языке Си или Си++ с
расширением «.с» или «.сpp» и генерирующий на выходе файл в перемещаемом
объектном коде «.o».
4) Компоновщик или Линкер – программа, объединяющая несколько исходных файлов в
перемещаемом объектном коде в один файл в неперемещаемом объектном коде, в
котором относительные адреса заменены абсолютными – все взаимные ссылки
программных модулей друг на друга и на библиотечные функции «разрешены» (в том
смысле, что символическим именам присвоены конкретные физические адреса). Все
кодовые секции программных модулей объединены в одну, как и секции данных
(инициализированных и неинициализированных).
5) Библиотекарь – программа, которая может объединить несколько оттранслированных/
откомпилированных и отлаженных программных модулей в перемещаемом объектном
коде в один файл с расширением «.lib» – специализированную пользовательскую
библиотеку, для последующего многократного использования, но уже без отладки, как
готового продукта.
6) Симулятор – программно-логическая модель целевого процессора, реализованная на
обычном компьютере и полностью имитирующая архитектуру и систему команд
целевого процессора. Симулятор не только имитирует процессор, но и некоторые из
периферийных устройств – системную периферию. Он позволяет отлаживать
программу исключительно на компьютере, без необходимости подключения к нему
целевой платы разрабатываемой микропроцессорной системы. Это особенно важно
для студентов и инженеров, самостоятельно повышающих свою квалификацию.
7) Загрузчик – загружает (размещает) программу в абсолютном объектном коде на
выполнение либо в память симулятора, либо в память реальной процессорной платы.
Это может быть оценочная плата, созданная производителем микроконтроллера для
оценки возможности решения прикладной задачи на данном микроконтроллере, или
132

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
плата, разработанная самим конечным пользователем, при условии, что в ней
реализован один или несколько необходимых для отладки интерфейсов (глава 5).
8) Отладчик – позволяет выполнять код программы в режиме прогона, по шагам, с
точками останова, при полном контроле текущего содержимого регистров процессора
и переменных в памяти. Позволяет оценить быстродействие программы в целом и
отдельных ее фрагментов (подпрограмм).
9) Утилиты преобразования формата выходного файла в другой формат, например, для
загрузки кода в программатор внешних постоянных запоминающих устройств (ПЗУ)
или внешней флэш-памяти.

8.4.2 Основные этапы разработки и отладки программного обеспечения
Для всех интегрированных сред разработки порядок разработки и отладки ПО
примерно одинаков и иллюстрируется на рис. 8.1.
1) Создается новый проект, для которого выбирается целевой микропроцессор или
микроконтроллер, то есть – кристалл, на базе которого будет реализована
микропроцессорная система пользователя. Файл проекта – это специальный файл,
который содержит информацию обо всех исходных файлах, написанных
пользователем/пользователями и подключенных к проекту. Имена исходных файлов и
места их расположения запоминаются в файле проекта вместе с дополнительной
информацией о настройках среды для работы с текущем проектом. Изделия
(процессоры и микроконтроллеры) большинства ведущих мировых производителей
включены в электронную базу фирмы Keil и могут быть просто выбраны из нее. Если
целевой микроконтроллер отсутствует в этой базе (как в случае с отечественными
производителями микроконтроллеров), достаточно выбрать из базы тип процессорного
ядра (в нашем случае Cortex-M3 или М4 или М4F). В этом случае будут
поддерживаться базовые средства отладки ПО в симуляторе. Для эффективной работы
с оригинальными периферийными устройствами заказывайте у производителей
микроконтроллеров дополнения к стандартному пакету Keil, расширяющие его
возможности.
2) Для выбранного целевого процессора, если желаете, можно включить в состав проекта
стартовый файл инициализации процессора StartUp, который любезно
предоставляется фирмой Keil в качестве помощи разработчикам. Как уже упоминалось
ранее, он представляет собой некий «шаблон» – основу для создания Вашего
собственного стартового файла. Если Вы уже работали с процессором и создали свой
собственный стартовый файл – включите его в проект. Основное назначение этого
файла – инициализация процессора и встроенных периферийных устройств.
3) Конфигурируется интегрированная среда – для нее устанавливаются нужные
параметры. В большинстве случаев для начала работы достаточно «параметров по
умолчанию». Только иногда потребуется коррекция (будем информировать читателя, в
каких конкретно случаях).
4) С использованием встроенного текстового редактора пакета создаются исходные
файлы проекта либо на языке Ассемблер, либо на С/С++. Это может быть всего один
файл, например, MyProg.s на Ассемблере, или несколько, в том числе на языке
высокого уровня С/С++. Как минимум, в состав любого проекта должен входить
стартовый файл SturtUp.s и файл с программой пользователя (например, MyProg.s).

133

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
5) Каждый из исходных файлов запускается на трансляцию, если он написан на
Ассемблере, или на компиляцию, если – на С/С++. Как Вы увидите, для этого
достаточно одного щелчка кнопкой мыши на панели команд интегрированной среды.
Если в исходных файлах будут обнаружены синтаксические ошибки, Вы получите
соответствующие уведомления. Более того, Вы сможете сразу перейти в нужную
строку исходного файла, чтобы исправить ошибку. Процесс повторной трансляции
или компиляции выполняется для каждого отдельного файла до тех пор, пока ошибок
в исходных текстах программных модулей не будет. В результате создается группа
файлов проекта в перемещаемом объектном коде.
Разработка
ПО
Текстовый
редактор
Файлы программных модулей на исходном языке
.s

.s

.s

.s

.c

Транслятор с
Ассемблера

.c

Компилятор
С/С++

Файлы программных модулей в перемещаемом объектном коде

.o

.o

.o

.o

.o

o

Компоновщик
(Линкер)
Файл выходного программного модуля в перемещаемом объектном
коде с «разрешенными» взаимными ссылками

.o
Загрузчик

Библиотекарь

Файл выходного программного
модуля в абсолютном объектном коде

Библиотечный файл в
перемещаемом объектном коде

Симулятор
Отладчик

.lib

Оценочная плата
или плата пользователя

Рис. 8.1 Порядок разработки программного обеспечения в интегрированной среде

134

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
6) Выполняется конфигурирование памяти целевого устройства. Параметры
интегрированной среды настраиваются в соответствии с реальными объемами кодовой
памяти и памяти данных целевого микропроцессора, чтобы программа-компоновщик
«знала», какими ресурсами она располагает – куда можно разместить код программы,
куда – данные. При отладке в симуляторе можно обойтись установками «по
умолчанию». Их достаточно для отладки простых учебных проектов.
7) Выполняется компоновка проекта, то есть все кодовые секции исходных файлов
объединяются в одну кодовую секцию, которая размещается по адресам доступной
памяти программ. Все инициализированные секции объединяются в одну общую
секцию инициализированных данных, а секции неинициализированных данных – в
одну общую секцию неинициализированных данных. Она размещается по адресам
доступной памяти данных.
8) Проект, допускающий отладку в симуляторе процессора, загружается в симулятор и
выполняется под управлением отладчика. В этом случае периферийные устройства,
которые имеются в составе целевого микроконтроллера, не должны использоваться.
Так отлаживаются все модули, выполняющие преимущественно обработку данных.
9) Полностью отлаженные модули в перемещаемом объектном коде могут быть
объединены в пользовательскую библиотеку как ряд наиболее употребительных
функций. В последующем Вы сможете автоматически включать код любой
библиотечной функции в выходной файл проекта. Для этого достаточно только
подключитьфайл библиотеки к проекту. Нужная функция будет автоматически
извлечена из библиотеки компоновщиком.
10) Последовательная трансляция программных модулей позволяет постепенно создавать
и отлаживать проекты. В начале работы в состав проекта может входить лишь
небольшое число файлов, которое далее, по мере отладки проекта, будет
увеличиваться. Разные исходные файлы могут создаваться и отлаживаться разными
программистами. Только после полноценной отладки отдельного программного
модуля программистом он передается менеджеру всего проекта и подключается к
проекту. Менеджер проекта (обычно руководитель отдела программного обеспечения)
отвечает за сборку всех исходных файлов и файлов библиотек в один общий проект и
его отладку.
11) Теперь наступает самый ответственный момент – исполнительный код проекта может
быть загружен в реальную микропроцессорную систему (оценочную плату или плату
разработанного Вами контроллера) и выполнен под управлением отладчика.
Проследите, чтобы необходимые для этого интерфейсы имелись на вашей плате (глава
5).
12) Обратите внимание на то, что любой останов программы вызовет останов в работе и
встроенных в контроллер периферийных устройств. Поэтому, такая отладка будет
полноценной отладкой в реальном времени только в режиме выполнения программы
целиком (в режиме прогона).
13) Если в процессе отладки ПО в изделии возникли ошибки, то выполняется процесс
редактирования (модификации) отдельных программных модулей.
14) На самом последнем этапе целевой контроллер встраивается в изделие (источник
питания, преобразователь и т.д.) и его аппаратная и программная части тестируются в
условиях реальной эксплуатации.

135

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
Представленная на рис. 8.1. схема разработки программного обеспечения может
несколько отличаться для разных сред разработки. Так, компоновщик, а также программабиблиотекарь могут получать на входе несколько файлов в перемещаемом объектном коде
«.o» и генерировать в первом случае сразу выходной файл для загрузки в
микроконтроллер, а во втором случае – выходной библиотечный файл.
В среде Keil выходной файл имеет особый формат и расширение «.axf». Он не
удобен для исследования в текстовых редакторах и используется только загрузчиком при
вызове отладчика.
1. В результате работы компоновщика создается выходной файл в
абсолютном объектном коде, который при вызове отладчика загружается
либо в программу-симулятор, либо в целевую плату (оценочную или
контроллер конечного пользователя) на выполнение под управлением
программы - отладчика.
2. Так как оценочная плата обычно имеет интерфейс JTAG для подключения отладчика, а
в обычных компьютерах такого интерфейса нет, то, кроме оценочной платы, придется
приобрести переходник со стандартного интерфейса компьютера в интерфейс JTAG –
переходник USB/JTAG и установить на компьютер драйвер этого переходника.
Современная интегрированная среда разработки программ для
микроконтроллеров IDE, такая как μVision, содержит все описанные выше
программные модули в одном пакете, объединенные удобной графической
средой, когда вызов нужной функции выполняется одним «щелчком» мыши.
Среда содержит и удобные средства настройки всех входящих в нее
программ (опций). Более того, в одной среде Вы можете разрабатывать программное
обеспечение для микроконтроллеров разных производителей, с разными периферийными
устройствами, пользуясь как Ассемблером, так и языком высокого уровня С/С++,
используя функции из большого количества специализированных библиотек, созданных
либо разработчиками процессоров, либо Вами самими, либо другими пользователями,
если они сделали их общедоступными.
Часто весь необходимый программисту комплекс программных средств (утилит)
называется одним словом toolchain (набор инструментов) или даже compiler (компилятор)
несмотря на то, что он фактически состоит из множества независимых программ,
объединенных графической средой в единую систему.

8.5 Окно интегрированной среды μVision5
Общий вид окна интегрированной среды μVision5 представлен на
рис. 8.2. Мы будем постепенно знакомиться с назначением входящих в
пакет инструментов. Отметим, что Вы можете задавать нужные команды
из расположенного в верхней строке меню, открывая конкретные меню:
File (Файл) – для работы с файлами; Edit (Редактор) – для работы с исходными файлами в
текстовом редакторе; View (Просмотр) – для вывода на экран окна с нужной в данный
момент информацией или для временного сокрытия этого окна; Project (Проект) – для
работы с проектами; Flash (Флэш) – для программирования флэш-памяти целевого
устройства; Debug (Отладчик) – для управления процессом отладки программы, в том
числе в симуляторе; Peripherals (Периферия) – для работы со встроенной периферией;
Tools (Средства) – для работы с дополнительными инструментами пакета; SVCS (Software
Version Control System) – для доступа к системе контроля версий; Window (Окна) – для

136

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
управления окнами среды, их перемещения, упорядочивания и др.; Help (Помощь) – для
получения справки по возможностям пакета, Ассемблеру, системе команд и др. При
щелчке кнопкой мыши соответствующее меню раскрывается, и появляется возможность
выбора нужной команды.

Рис. 8.2 Общий вид окна интегрированной среды μVision
Две расположенные ниже панели инструментов содержат ряд наиболее
употребительных команд в виде пиктограмм. Так, в первой из них Вы наверняка узнали
пиктограммы «Создать файл», «Открыть файл», «Сохранить», а также ряд пиктограмм,
которые являются общепринятыми во всех текстовых редакторах «Вырезать»,
«Скопировать», «Вставить» и др. Некоторые пиктограммы в этой строке будут широко
использоваться при отладке, например, для установки и снятия точек останова.
Во второй панели инструментов расположены команды, которые управляют
процессом трансляции (компиляции) и сборки файлов проекта.
На всем остальном поле могут располагаться окна с различной информацией
(программами в исходных кодах, содержимым нужных при отладке областей памяти и
т.д.). Пока Вы видите два из них – окно проекта Project и окно результата создания
(построения) выходного файла Build Output. В последнее окно будут выводиться
результаты трансляции файлов Вашего проекта и компоновки. Здесь же отображаются
сообщения об ошибках и предупреждения.
В самой верхней строке всегда присутствует имя файла текущего проекта, которое
имеет расширение «.uvprojx» с указанием полного пути доступа к проекту. Этот файл
имеет специальный формат. Он содержит информацию обо всех программных модулях
проекта, целевом процессоре, опциях (настройках) среды и так называемых параметрах
окружения среды. Это означает, что при повторном открытии проекта для продолжения
работы с ним, Вы увидите на экране те же окна, которые Вы открывали ранее, в самом
последнем сеансе работы. Авто-возвращение к последней используемой конфигурации
среды существенно экономит время программиста на настройку среды при отладке ПО.

137

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
В пакете μVision используются общепринятые в компьютерах
технологии работы с окнами и командами, как в виде так называемых
«выпадающих» меню, так и в виде панелей инструментов с пиктограммами
наиболее важных команд. Для выполнения нужной команды достаточно
просто «щелкнуть мышью» на соответствующей пиктограмме. Команда
будет немедленно выполнена.

8.6 Назначение псевдокоманд – директив Ассемблера
Ассемблер – это язык мнемокодов команд процессора. Программа
состоит из последовательности команд, каждая из которых записывается в
отдельной строке. Однако, наверняка у Вас уже возник ряд вопросов: где
будет находиться написанный код в целевом процессоре после трансляции
программы на Ассемблере? Можно ли разместить в кодовой памяти
константу или таблицу констант, например, значения опорных точек
некоторой функции? Как выделить оперативную память под переменную или массив
переменных? Как обратиться к таким переменным? Как подключить к проекту важную
функцию, которую написал коллега-программист и отладил ее? Как воспользоваться
приобретенной дополнительно библиотекой функций? И таких вопросов – множество.
Поэтому, кроме основной задачи по автоматическому переводу ассемблерных
команд в машинные коды, язык Ассемблер предоставляет целый ряд возможностей по
управлению процессом трансляции и компоновки программы с помощью так называемых
директив или псевдокоманд Ассемблера. Директивы записываются в поле мнемокодов
команд и могут иметь, как и команды процессора, один или несколько операндов, а также
необязательные опции, специфицирующие их выполнение. Но, в отличие от команд
процессора, директивы не транслируются в машинный код. Это лишь указания
транслятору или компоновщику на выполнение ими нужных для программиста действий.
Как и любой язык программирования, Ассемблер имеет свой синтаксис. В его
основе – синтаксис команд процессора и синтаксис псевдокоманд. Основное назначение
псевдокоманд: структурирование программных модулей; резервирование памяти под
переменные и константы; помощь программисту в генерации сложных команд, когда
псевдокоманда более понятна и автоматически заменяется транслятором на фактически
имеющуюся команду, синтаксис которой сложен. С их помощью можно выполнить
условное ассемблирование программы в зависимости от требуемой в данный момент
версии ПО, расширить систему команд процессора с использованием макросредств.
Кроме того, Ассемблер предоставляет дополнительные возможности по автоматическому
вычислению арифметических и логических выражений в любой системе счисления
непосредственно на стадии трансляции программы, освобождая программиста от
рутинной работы с калькулятором.
Еще раз отметим, что в этой книге используется Ассемблер ARM ASM, который
может несколько отличаться от языков Ассемблер других производителей (GCC, IAR).
Эти отличия касаются, главным образом, набора поддерживаемых языком псевдокоманд и
их синтаксиса. Мнемокоды собственно команд процессоров ARM во всех версиях языка
Ассемблер должны быть одинаковыми.

8.7 Использование символических имен в программе
К символическим именам, используемым в программе на Ассемблере, относятся:

138

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
1) Метки определенных фрагментов кода, на которые может быть передано управление,
например, имена точек входа в подпрограммы. Значение такого имени – фактический
адрес расположения команды, помеченной меткой. Метка записывается с самой
первой позиции ассемблерной строки и отделяется от поля команд/директив пробелом
или группой пробелов. В ассемблере ARM ASM двоеточие после имени метки (как это
делается в ряде Ассемблеров других фирм) не ставится.
2) Символические имена (идентификаторы) констант или переменных, значения которых
соответствуют адресу их фактического расположения в памяти МПС;
3) Символические имена констант, значения которых только сообщаются Ассемблеру,
но память под них не выделяется.
Вы можете определить символическое имя константы, сообщаемой
Ассемблеру с помощью директивы EQU (Эквивалентно). Эта директива
подобна широко используемой в языке С/С++ директиве определения
констант #define. Ее синтаксис в наиболее употребительной форме:
Name EQU expr
Здесь:
Name – символическое имя константы;
expr – арифметическое выражение, результатом вычисления которого может быть
32-разрядное целое число, 32-разрядный абсолютный адрес или величина смещения
(offset) адреса относительно текущего значения в регистре-указателе (register-relative
value) или в счетчике команд (PC-relative value);
Примеры:
a
EQU 2
b
EQU 0x34
c
EQU a+b
;…
MOV r0, #a

; Определить константу a (a=2)
; Определить константу b (b=0x34=52)
; Определить константу c=a+b=54
; Загрузить константу a в регистр r0

Как только константа определена, ее можно использовать в любом месте
программы в качестве непосредственного операнда, как показано выше на примере
загрузки непосредственными данными регистра ЦПУ r0.
Символические имена никогда не включаются в машинный код программы, но
позволяют повысить ее «читабельность», упростить и облегчить дальнейшее
сопровождение и модификацию. Поэтому, для обозначения переменных рекомендуется
использовать символы, которые имеют смысл для разработчика, например, Speed
(скорость), а для обозначения названий подпрограмм (точек входа в них) – символические
имена, отражающие выполняемые подпрограммой действия, например, CALC_y1
(вычисление булевой функции y1). Они будут дополнительным комментарием к
программе. При выборе имен следует соблюдать следующие правила:
• Имя внутри текущего модуля должно быть уникальным. Двойное определение
одинаковых имен не допускается;
• Имя может быть записано буквами в верхнем или нижнем регистре, или и в том, и в
другом;
• Ассемблер чувствителен к значению регистра, поэтому переменные «Var» и «var» с
точки зрения транслятора являются различными;

139

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ


Рекомендуется использование составных имен, разделителем в которых является
символ подчеркивания, например, Var_low (младшее слово переменной Var);
• По умолчанию нет никаких ограничений по длине имени, но слишком длинные имена
нежелательны;
• Первый символ в имени обязательно должен быть буквой, далее можно использовать
цифры, например, a_1;
• Нельзя использовать зарезервированные в Ассемблере символы (мнемокоды команд,
директив ассемблера и т.д.);
• Точка «.» может использоваться в именах, в том числе в смысле разделителя
составного имени, например, a.high (старшая часть переменной «а»);
• Для выделения важнейших, с точки зрения разработчика, имен, допускается заключать
их в своеобразные скобки с помощью вертикальных линий «|» слева и справа от
имени, например, | .text |. При этом транслятор игнорирует такие «скобки» и заносит в
свою таблицу символов просто имя «.text».
Использование символических имен обеспечивает символьную отладку
программы, когда пользователь обращается к нужным областям памяти в процессе
тестирования программы не по их фактическим адресам, а по именам переменных,
которые в них содержатся.

8.8 Секционирование программных модулей
Любой проект обычно состоит из нескольких программных модулей.
Каждый программный модуль может содержать различную информацию:
код программы; данные, расположенные в кодовой памяти, например,
табличные значения функций; переменные, расположенные в оперативной
памяти; специальную область памяти для временного хранения данных, в
том числе – область стека. Разные по назначению области памяти
программных модулей называются секциями. В программном модуле может быть любое
число секций.

8.8.1 Директива объявления секции
Прежде, чем использовать секцию в программном модуле, ее
необходимо объявить. Для этой цели предназначена специальная директива
Ассемблера AREA (Область, Секция), имеющая следующий синтаксис:
AREA Section_Name {,type } {,attr } …
В поле операндов директивы AREA указывается имя объявляемой секции
Section_Name, через запятую – ее тип type и далее необязательные опции, атрибуты attr,
также перечисляемые через запятую.
Существуют всего два типа секций:
• CODE – кодовая, которая может быть расположена только в памяти программ;
• DATA – данных, которая может быть расположена только в памяти данных.
Таким образом, секции – это поименованные программистом и полностью
независимые друг от друга области кодовой памяти (тип CODE) или памяти данных (тип
DATA). Ими может манипулировать программа-компоновщик, объединяя однотипные
секции (с одним и тем же именем) в одну общую секцию с тем же именем. Более того,
140

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
компоновщик размещает все секции, имеющие тип CODE, в кодовой памяти, а все секции,
имеющие тип DATA – в памяти данных. Естественно, что для этого ему требуется
информация о фактической карте памяти используемого микроконтроллера, которую
обязательно должен сообщить среде разработки программист. Эта информация будет
использоваться и для контроля заполнения памяти программ и памяти данных по мере
разработки проекта (включении в его состав новых программных модулей по мере их
написания и отладки). В случае переполнения имеющегося в микроконтроллере объема
памяти будут выдано сообщение об ошибке.
Все команды процессора или директивы Ассемблера, написанные после директивы
AREA, будут принадлежностью текущей объявленной секции до тех пор, пока не будет
объявлена новая секция. Никакого маркера конца текущей секции нет. Как только
объявляется новая секция, старая секция заканчивается.
В кодовых секциях может располагаться код программы, константы и таблицы
констант. В секциях данных могут располагаться только переменные. Для их
резервирования с указанием имен переменных и размера используются специальные
директивы Ассемблера (см. ниже).
В отличие от языков программирования высокого уровня, где
объявления переменных могут чередоваться с командами (операторами
языка), в ассемблере требуется четкое разделение этих областей программы
– инструкции могут находиться только в кодовых секциях, а описания
переменных – в секциях данных. С точки зрения языка Ассемблер, секция
представляет собой однородную область памяти, в которой может находиться только
однотипная информация: коды команд или переменные (в том числе – стек).
В директиве объявления секции AREA можно использовать дополнительные
атрибуты, являющиеся необязательными опциями:
• readonly (только для чтения) – секция, предназначенная исключительно для чтения
данных (это атрибут кодовой секции CODE по умолчанию);
• readwrite (для чтения и записи) – это атрибут секции данных по умолчанию;
• noinit (без инициализации) – используется для секции данных типа DATA и
означает, что область памяти не инициализируется или инициализируется нулем
(0). Эта секция может содержать только директивы резервирования памяти без ее
инициализации или директивы резервирования и одновременного обнуления
памяти;
• align=n со значением n от 0 до 31. Опция выравнивания адреса начала секции по
модулю 2n. Показывает, по какому адресу секция должна быть выровнена в памяти.
Так, при n равном 0, 1, 2 выравнивание будет выполняться соответственно: по
байту (нет выравнивания), по полуслову (двум байтам), по слову (четырем байтам).
Фактически n – это число младших бит начального адреса секции, которые должны
быть нулевыми. Выравнивание означает пропуск определенного числа байт в
памяти до начального адреса расположения секции.
Один из часто используемых атрибутов – атрибут выравнивания align=n. Если он
отсутствует, то по умолчанию секции выравниваются по границе 32-разрядного слова – по
4-м байтам. Фактический список атрибутов директивы AREA значительно шире. С ним
можно ознакомиться, обратившись в справочную систему пакета μVision5.
Для каждой секции программного модуля в трансляторе с Ассемблера
предусматривается свой собственный счетчик адреса размещения данных. С началом
141

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
секции этот счетчик автоматически обнуляется. По мере записи в текущей кодовой секции
новых команд его содержимое автоматически инкрементируется на длину команды в
байтах (на 2 или 4 байта). При резервировании и инициализации в кодовой памяти
констант, значение счетчика увеличивается на длину константы. Если текущая секция
является секцией данных, то с каждой командой резервирования в ней переменных
значение счетчика адреса размещения автоматически увеличивается на длину
зарезервированных данных в байтах.
Используйте директиву AREA для разделения любого исходного
файла с программой на Ассемблере на отдельные секции. Секции с
одинаковыми именами могут объявляться много раз как в одном и том же
программном модуле, так и в разных. Все они будут объединены
компоновщиком в одну общую секцию с тем же именем. Результирующая
секция получит атрибуты первой секции, объявленной директивой AREA.
Обязательно разделяйте кодовые секции и секции данных. Помните, что в
кодовой секции можно разместить не только текст программы, но и константы, таблицы
констант. При большом объеме однотипных данных выделите для них специальную
кодовую секцию, например, с именем MyTable. Аналогично поступайте с секциями для
размещения переменных. Делите их на функциональные группы и выделяйте для них
отдельные секции. Так, специальную секцию данных желательно иметь для системного
стека.
1) Допустима ли программа, в которой не объявлено ни одной секции?
2) Можно ли в секции данных разместить мнемокод команды?
3) Какая область памяти данных называется не инициализированной?
4) С какой целью используется выравнивание памяти?
5) По какой границе будет выполнено выравнивание памяти для
очередной кодовой секции, если для нее задан атрибут align=10?
6) Зачем может понадобиться такое выравнивание, как выше?
1) Нет. В любом программном модуле должна быть объявлена, по
крайней мере, одна секция.
2) Нет.
3) Такая, в которой для переменных просто отводится определенное
место в памяти, но запись начальных значений не производится.
4) Процессоры ARM поддерживают доступ и к не выровненным
данным (байтам, полусловам), но расположенным в памяти данных. При этом за
счет исключения «пропусков» обеспечивается более рациональное использование
имеющейся памяти данных конкретного микроконтроллера. При доступе к кодам
команд атрибут выравнивания align=0 (по байту) или align=1 (по полуслову)
использовать нельзя. Оптимальная скорость конвейера команд обеспечивается при
выравнивании памяти по слову (4-м байтам). В этом случае на конвейер команд
считывается сразу 32-разрядное слово – одна команда набора ARM или сразу две
команды набора Thumb.
5) По 210 = 1024 байтам, т.е. по границе 1 К байта.
6) Например, для того, чтобы зарезервировать некоторый объем имеющейся памяти в
системе для последующего возможного расширения.

142

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
Для названий секций можно использовать любые имена. Однако, если
это название начинается не с буквы (например, с цифры), то оно должно быть
ограничено символами вертикальной черты «|», например, |1_Data|.
Некоторые имена секций являются зарезервированными. Так, секция |.text|
считается кодовой секцией, которая создается СИ-компилятором. В ней могут
располагаться также библиотечные функции языка СИ.

8.8.2 Директива DCI размещения констант в кодовой секции
В кодовой памяти вместе с кодами операций (собственно программой)
могут
размещаться
и
предварительно
проинициализированные
программистом данные – отдельные константы или таблицы констант, к
которым по ходу выполнения программы могут следовать обращения. Это
удобно, так как обращения возможны по символическим именам, а не по адресам
фактического размещения констант в памяти.
Для резервирования констант в памяти программ (в секциях типа CODE)
предусмотрена директива Ассемблера:
{label} DCI{.W} expr{,expr}
Она может начинаться с необязательной метки, за которой следует мнемокод
директивы DCI (от англ. Define Code, Initialization – Определение кода и инициализация),
а далее – одно или несколько арифметических выражений expr, отделенных друг от друга
запятой. Значения этих выражений, вычисленных на стадии трансляции программы, и
будут константами, размещаемыми в памяти. Сколько констант будут представлены в
строке операндов директивы, столько и будет размещено в памяти (ограничений нет). Вы
можете последовательно использовать несколько таких директив для размещения в
кодовой памяти целой таблицы опорных точек некоторой функции. Первая константа
автоматически получает символический адрес, соответствующий метке label. Данные в
памяти будут выровнены по границе полуслова или полного слова, как это и должно быть
в кодовой памяти. Эта директива используется для:
1) Размещения в кодовой памяти отдельных констант и таблиц констант;
2) Для создания таблиц точек входа в процедуры пользователя (начальных адресов этих
процедур);
3) Для создания в кодовой памяти зон с начальными значениями переменных, которые
должны быть в последующем установлены в ОЗУ (массива копий значений
инициализируемых переменных в ОЗУ). В процессе выполнения стартового файла эти
значения должны автоматически копироваться в нужные области ОЗУ и, тем самым,
выполняться инициализация переменных.
4) Директива DCI может использоваться также для ввода кодов команд, которые не
поддерживаются текущей версией языка Ассемблер. Это одно из ее
преимущественных областей применения.
Что означает опция команды {.W}? Она определяет формат
инициализируемых данных. Если она отсутствует, то данные представляют
собой 16-разрядные полуслова, в том числе коды команд набора Thumb. Если
опция указана, то резервируются данные в формате полного 32-разрядного слова (в том
числе коды команд набора ARM).

143

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
Примеры:
Const_1
Tabl_y1
Const_2

DCI 0x44
; Резервирование константы 0x44
DCI 0,1,2,3,4,5,6,7 ; Резервирование таблицы констант
DCI.W 0x44
; Резервирование константы 0x44
; в формате 32-разрядного слова
; (принудительно)

8.8.3 Директива резервирования переменных в секциях данных SPACE
Имеется специальная директива Ассемблера, которая предназначена
для выделения места в секциях данных (типа DATA) под переменные
текущего программного модуля – резервирования места под переменные
проекта. Она выделяет необходимое место в памяти для переменной без ее
предварительной инициализации. Если инициализация необходима – ее выполняет
программист в своей программе.
Директива SPACE (от англ. место, пространство) имеет следующий синтаксис:
{label} SPACE expr
Метка label в начале директивы задает символическое имя резервируемой
переменной, выражение expr – число байт в памяти, выделяемой для нее (вычисляется на
этапе трансляции). Директива может использоваться также для выделения места под
буфер данных заданной длины или под какую-либо структуру данных. В этом случае
первая ячейка буфера будет иметь символический адрес label. Если выражение expr не
задано, то метка просто получает значение текущего адреса в памяти данных
(резервируется как бы нулевая область памяти).
Примеры:
N
Var1

EQU 20
SPACE 4

;
;
;
Var2
SPACE 4
;
Buffer_32 SPACE 4*N ;
HW_1
SPACE 2
;
HW_2
SPACE 2
;
B_1
SPACE 1
;
B_2
SPACE 1
;

Число слов в буфере данных
Выделить место в ОЗУ под переменную Var1
(32-разрядное cлово)
Под переменную Var2
Под буфер из 20-и 32-разрядных слов
Под полуслово HW_1
Под полуслово HW_2
Под байтовую переменную B_1
Под байтовую переменную B_1

Как видите, в выражениях допускается использование ранее определенных
символических имен (в нашем случае – N). Вычисления значений арифметических
выражений выполняется автоматически на стадии трансляции программного модуля.
Директива SPACE просто выделяет место в оперативной памяти под
переменные. Это так называемые неинициализированные переменные.
Обычно они автоматически обнуляются при сбросе процессора. Их
инициализацию, если это необходимо, должна выполнить программа
пользователя, что и делается в большинстве программ на Ассемблере.
Ниже мы рассмотрим группу директив, которые могут проинициализировать
переменные начальными значениями не только в ПЗУ, но и в ОЗУ. Последнее возможно
только в том случае, когда в стартовый файл проекта включены специальные процедуры
копирования «констант инициализации» из области ПЗУ в область ОЗУ.
144

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
Технология такова: сначала необходимо создать «образ» инициализируемых
переменных в ПЗУ, а затем, перед тем как запустить программу пользователя на
выполнение, автоматически скопировать этот образ из ПЗУ в определенные области ОЗУ.
Первую часть задачи выполняет транслятор, размещая копии инициализированных
переменных в служебную область ПЗУ вместе со служебной информацией (начальные
адреса областей памяти в ОЗУ, объемы данных). Вторая часть задачи – стартовый файл
проекта. В нем выполняется анализ числа инициализируемых переменных и места их
размещения. «Образ» инициализируемых переменных, находящийся в кодовой памяти,
копируется в ОЗУ по нужным адресам. Только после этого управление передается
программе пользователя, которая будет иметь дело уже с проинициализированными
переменными. Реализация такой технологии возможна только при наличии специального
стартового файла.
На начальной стадии освоения курса программирования на Ассемблере мы не
будем использовать эту технологию, чтобы не усложнять стартовый файл и сделать его
понятным для новичков. Будем применять директивы Ассемблера, описанные ниже,
исключительно для размещения констант и таблиц констант в кодовой памяти. При
необходимости инициализации переменных в ОЗУ, нужные константы будем копировать
из ПЗУ в ОЗУ соответствующими командами процессора непосредственно в теле
пользовательских программ.
Отметим тем не менее, что предварительная инициализация переменных в ОЗУ
широко применяется в программах на языке высокого уровня С/С++. В этом
случае прежде, чем передать управление основной программе пользователя
main(), в стартовом файле проекта выполняются необходимые
предварительные действия по копированию «образа» констант из ПЗУ в ОЗУ.
Стартовый файл разрабатывается на Ассемблере квалифицированными специалистами и
предоставляется программистам на языке С/С++ в готовом виде фирмами –
разработчиками компиляторов.

8.8.4 Директива заполнения области памяти константой FILL
Эта директива не только резервирует определенный промежуток
области памяти (кодовой или данных), но и заполняет его заданной константой
(выполняет инициализацию переменных):
{label} FILL expr {,value {,valuesize}}
Она эквивалентна директиве резервирования места в памяти SPACE, но с загрузкой
в указанную область памяти длиной в байтах, заданной выражением expr, серии
одинаковых значений value («шаблона»). При этом размер «шаблона» заполнения памяти
определяется параметром valuesize (число байт в «шаблоне», 1, 2 или 4). Если опции в
фигурных скобках не указаны, то заполнение памяти выполняется нулями (память
принудительно обнуляется).
Размер константы заполнения («шаблона») может отсутствовать. Но, если он
указан, то общий объем резервируемой памяти в байтах, заданный выражением expr,
должен быть кратен размеру «шаблона» valuesize. Если размер «шаблона» не указан, Вы
можете записать перед значением value лидирующий ноль, чтобы все константы стали 32разрядными.

145

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
Примеры:
Var_A

FILL 4, 25, 4

Buffer_8

FILL 16, 0xFF, 1

Buffer_32 FILL (10*4),0xFF

;
;
;
;
;
;

Инициализация одного слова Var_A
значением 25
Заполнить 16 байт в памяти
константой 0xFF
Заполнить 10 слов в памяти
константой 0x000000FF

Напомним, что заполнение секции данных (DATA) константами требует
стартового файла определенной структуры.

8.8.5 Директивы размещения в памяти констант в заданном формате
Директивы DCB, DCW, DCD, DCQ (от англ. Define Code – определить код)
обеспечивают резервирование памяти под константы в ПЗУ и переменные в ОЗУ типа
байт (суффикс «B»), полуслово (суффикс «W»), полное 32-разрядное слово (суффикс
«D»), двойное 64-разрядное слово (суффикс «Q»). Они могут применяться как в кодовой
секции для резервирования констант и таблиц констант, так и в секции данных для
инициализации переменных. В последнем случае необходим специальный стартовый
файл, поддерживающий инициализацию переменных в ОЗУ (см. выше). Директивы имеют
следующий синтаксис:
{label}
{label}
{label}
{label}

DCB expr1 {,expr2}{,expr2}…
DCD{U} expr1 {,expr2}{,expr3}…
DCW{U} expr1 {,expr2}{,expr3}…
DCQ{U} expr1 {,expr2}{,expr3}…

Директива резервирования байт DCB обеспечивает их последовательное
размещение в памяти без какого-либо выравнивания адресов, директива резервирования
полуслов DCW выполняет автоматическое выравнивание по четным адресам, а директива
резервирования слов DCD – автоматическое выравнивание по адресам, кратным числу 4
(дважды четным). Такое же выравнивание адресов памяти (по 4-м байтам) выполняется и
при резервировании двойных слов директивой DCQ.
Суффикс «U» используется для указания опции отсутствия выравнивания. В этом
случае данные располагаются в текущей секции подряд, без каких-либо пропусков.
В поле операндов каждой из этих директив можно указать одно или несколько
выражений, значения которых будут вычислены на стадии трансляции и записаны в
соответствующие ячейки памяти в качестве начальных значений переменных.
Метка label, как обычно, является символическим именем первой переменной.

146

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
Примеры:
; Объявить секцию данных по имени MyData
AREA
MyData, DATA, READWRITE
; Зарезервировать три переменные типа слово с инициализацией
Var_1
DCD
0xff33, 37, 0xffaa55bb
; Зарезервировать 200 байт под буфер BUF_0
BUF_0
SPACE
200
; Все ячейки будут обнулены
; Зарезервировать еще 50 байт
; заполнив все байты константой 0x5A
; Будут проинициализированы числом 0x5A
BUF_1
FILL
50, 0x5A, 1
В этой книге директивы DCB, DCW, DCD будут использоваться
только для резервирования констант в кодовой памяти. Это позволит
использовать более простую и понятную для начинающих структуру
стартового файла (глава 9).

8.9 Автоматическое вычисление выражений транслятором
В ряде приведенных выше примеров использовались символические
имена переменных, и транслятору с Ассемблера поручалась роль
калькулятора для расчета нужных программисту значений. Автоматическое
выполнение расчетов на стадии ассемблирования программы существенно
облегчает программирование и позволяет избежать множества описок, свойственных
человеку, что повышает надежность программного обеспечения.
В выражениях можно использовать не только символические имена переменных и
констант, но и значения меток. В последнем случае выражения будут не абсолютными, а
относительными – их окончательные значения будут определены не на этапе
ассемблирования конкретного программного модуля, а на этапе компоновки проекта.
В выражениях можно использовать операторы, связывающие между собой числа и
ранее определенные символические имена. Таблица основных операторов в Ассемблере и
их аналогов в языке высокого уровня С/С++ приведена ниже. Операторы указаны в
порядке приоритета их выполнения, если он не изменяется круглыми скобками. Самый
высокий приоритет имеют операторы в верхней части таблицы. При равенстве
приоритетов операции выполняются в порядке слева направо.

147

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
Таблица 8.1 Основные операторы языка Ассемблер и их аналоги в языке С/С++
Операторы языка Ассемблер

Эквивалентные
операторы в С/С++

Унарный минус
Умножение
Деление нацело
Остаток от деления
Сдвиг влево логический
Сдвиг вправо логический
Сдвиг влево циклический
Сдвиг вправо циклический

1
2

3

*
/

:MOD:
:SHL:
:SHR:
:ROR:
:ROL:

Числовые операции над двумя операндами:
Сложение
Вычитание
Логическое побитовое «И»
Логическое побитовое «ИЛИ»
Логическое побитовое «Исключающее ИЛИ»

4

Операции сравнения:
Равно
Больше
Больше или равно
Меньше
Меньше или равно
Не равно

5

Логические (булевы) операции
операндами:
Логическое И
Логическое ИЛИ
Логическое Исключающее ИЛИ

6

с

*
/
%
>

+
:AND:
:OR:
:EOR:

+
&
|
^

=
>
>=
<

>=
<
n) ?
#(m = n) ?

148

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
1)
2)
3)
4)
5)
6)
7)

10
0xF5
0
0xF5
0x14
1 (True)
0 (False)

8.10 Внутренняя и внешняя модульность программы
8.10.1 Внутренняя модульность
Мы уже рассматривали ранее понятие внутренней модульности
программы, (см. 7.7), состоящей из основной программы и некоторого
числа подпрограмм (функций), которые могут многократно вызываться по
мере выполнения основной программы. Под модулем понимается
отдельный файл с программой на Ассемблере, который может
транслироваться независимо от остальных файлов. Если отдельные
фрагменты программы или подпрограммы находятся внутри текущего модуля, то доступ к
ним возможен без каких-либо ограничений. Достаточно, например, пометить начала
фрагментов программ или подпрограмм символическими именами и передавать им
управление командами B label (brach) – перехода на метку или командой BL label (branch
with link) – перехода на начальный адрес подпрограммы с сохранением адреса возврата в
регистре связи LR.
Все метки и переменные, объявленные в текущем модуле, являются локальными
метками - local, доступными исключительно изнутри и недоступными из других модулей.
Это предотвращает любое несанкционированное программистом вмешательство внутрь
текущего программного модуля извне, в том числе, вызов имеющейся в этом модуле
подпрограммы.
Такой модуль представляет собой своеобразный «черный ящик», в котором может
быть любое число различных секций, структурирующих программу, и любое число
подпрограмм. Под внутренней модульностью далее будем понимать: 1) структурирование
программы кодовыми секциями и секциями данных; 2) наличие в модуле основной
программы из любого числа фрагментов, которым может передаваться управление, и
любого числа подпрограмм. В частном случае программный модуль может содержать
только основную программу или подпрограмму (или несколько подпрограмм).
В общем случае каждый программный модуль должен иметь некоторый интерфейс
с другими модулями (внешними по отношению к нему), если программист предполагает
их совместную работу. Об этом – далее.

8.10.2 Директивы объявления функций PROC и FUNCTION
В отличие от языков высокого уровня, в Ассемблере понятие функции не
отличается от понятия процедуры. И то, и другое – подпрограмма, которая получает на
входе некоторый набор аргументов, обрабатывает его и выдает (возвращает) результат в
виде значений одной или нескольких переменных (возможно даже – массива данных).
Подпрограммы могут вызываться многократно с разными наборами аргументов,
генерируя различные результаты. С точки зрения Ассемблера, функция – это та же
подпрограмма, но возвращающая лишь один результат.

149

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
Для идентификации начала и конца подпрограммы в Ассемблере предусмотрены
специальные директивы: PROC (объявление процедуры) и ENDP (конец текущей
процедуры) или FUNCTION (объявление функции) и ENDF (конец функции). Эти
директивы равноценны и представляют собой своеобразные необязательные операторные
скобки, в которые может быть заключено тело любой процедуры:
Label
PROC
; Тело подпрограммы
;

ENDP

или

Label
FUNCTION
; Тело функции
;

ENDFUNC

Вы можете не использовать при оформлении процедур специально
предназначенные для этого директивы. Однако лучше все же воспользоваться этой
возможностью. В этом случае Ваш программный модуль будет более читабельным. Кроме
того, по мере роста Вашей квалификации, Вы сможете задавать в директивах
PROC/FUNCTION дополнительные опции для контроля правильности передачи
параметров в подпрограммы на этапе отладки. Технологии передачи параметров в
подпрограммы и вложенным подпрограммам будет посвящен отдельный раздел книги
(глава 15).

8.10.3 Внешняя модульность
Ситуация модуля – «черного ящика» – существенно меняется, когда подпрограмма
или переменная, к которой следует обращение, находится в другом программном модуле
или в библиотечном файле. В этом случае программист должен «предупредить»
транслятор, что указанное им имя является по отношению к текущему модулю внешним.
С другой стороны, в том программном модуле, где находится переменная или метка, к
которой может последовать обращение извне, необходимо сообщить транслятору, что
этот символ должен быть «общедоступным» (глобальным).
Только при таком подходе при объединении файлов проекта компоновщиком не
возникнет конфликта имен: при трансляции исходных файлов эти имена будут помечены
как внешние или глобальные (без присвоения им конкретных адресов), а при компоновке
и размещении выходного исполнительного модуля в памяти они получат конкретные
значения. Программисты говорят, что «разрешение» всех внешних ссылок выполняется не
на стадии трансляции программных модулей, а на стадии их компоновки. При этом
термин «разрешение» внешних ссылок используется в смысле расчета и присвоения
символическим именам конкретных физических адресов в соответствии с окончательным
реальным размещением кодовых секций и секций данных в памяти микроконтроллера.
Имеются две директивы Ассемблера, которые управляют видимостью
переменных. Их можно располагать в любом месте программы, однако
правилом хорошего тона при разработке ПО считается размещение таких
директив в начале каждого программного модуля. В этом случае они будут
отражать интерфейс текущего программного модуля с внешним миром (другими
программными модулями).
8.10.3.1 Директива объявления глобального (общедоступного) имени
Если разработчик считает, что какие-либо символы должны быть доступны из
других модулей (разрешает внешний доступ к ним), то он должен пометить эти символы

150

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
как глобальные, доступные отовсюду с помощью директивы Ассемблера EXPORT
(«экспортировать имя» вовне). Ее упрощенный синтаксис, используемый наиболее часто:
EXPORT symbol [WEAK, type]
Имя директивы EХPORT размещается, как обычно, в полемнемокода команды. За
ним, после пробела, указывается имя глобального символа symbol, который должен быть
доступен извне. В квадратных скобках могут быть указаны приведенные ниже опции:
Опция WEAK («Слабый»): используется преимущественно в описании точек входа
в подпрограммы обслуживания прерываний/исключений на начальной стадии разработки
ПО, когда вместо реальной процедуры обслуживания прерывания/исключения, которая
еще не создана, используется процедура шаблон – как бы «пустышка», которая, ничего не
делая, просто возвращает управление обратно или реализует бесконечный цикл (лишь
фиксирует факт попадания в процедуру обслуживания прерывания без выполнения какихлибо конкретных действий). Это позволяет выполнить начальную инициализацию
таблицы векторов прерываний/исключений, которая должна обязательно присутствовать в
начальной области кодовой памяти для работы любой программы (см. 9.1). В дальнейшем,
возможно уже в самом конце разработки, Вы напишете программный модуль, в котором
определите настоящую процедуру обслуживания прерывания/исключения с точно таким
же именем, но без опции «слабого» символа WEAК. В этом случае произойдет следующее
– компоновщик поймет, что появилось более «сильное» глобальное имя и заменит имя
процедуры-шаблона
на
имя
реальной
процедуры.
В
таблицу
векторов
прерываний/исключений будет включен другой адрес. Использование этой опции
позволяет не удалять из программного модуля процедуру-шаблон, заменяя ее на новую
автоматически, без каких-либо конфликтов многократного определения имен.
Опция type задает тип секции, к которой относится данная глобальная метка.
Возможны лишь два варианта, в соответствии с двумя возможными типами секций:
• DATA – метка будет расположена в секции данных (глобальная переменная);
• CODE – метка будет расположена в кодовой секции (точка входа в
общедоступную подпрограмму или метка константы/таблицы констант);
8.10.3.2 Директива объявления внешнего имени
Если в текущем программном модуле имеется обращение к процедуре,
расположенной в другом программном модуле, выполняется передача управления в
другой модуль или выполняется доступ к переменной, расположенной в другом модуле по
записи или чтению, то соответствующее имя должно быть помечено как внешнее. Для
этой цели используется директива IMPORT («импортирование» имени извне) с
синтаксисом:
IMPORT symbol [WEAK, type]
Если опция «WEAK» не специфицирована (как это бывает в большинстве случаев)
и указанный в директиве внешний символ при компоновке модулей проекта не найден, то
компоновщик генерирует сообщение об ошибке.
Если же при определении внешнего имени указана опция «WEAK», а символ в
процессе компоновки не найден, то возможны следующие варианты:
1) Команда перехода (B) или вызова подпрограммы (BL) автоматически заменяется
командой NOP (нет операции) и фактически выполняется переход на или в?
следующую команду программы;
151

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
2) В других командах (например, загрузки данных) адрес внешней метки
автоматически заменяется нулем. Этот механизм может, в частности,
использоваться для условного ветвления программы в зависимости от того,
подключена ли к проекту библиотека с нужной подпрограммой или нет.
Тип переменной type, так же, как и в директиве EXPORT, определяется типом
секции, в которой дальше будет объявляться переменная (DATA, CODE). Он указывается
лишь тогда, когда описание внешних интерфейсов модуля выполняется в самом его
начале, еще до объявления конкретной секции.
В большинстве применений директивы EXPORT и IMPORT могут не
содержать опции «WEAK». Эта опция в директиве EXPORT обязательно
потребуется
при
создании
шаблонов
процедур
обработки
прерываний/исключений для того, чтобы можно было с самого начала
работы над любым проектом сгенерировать таблицу векторов перехода на
подпрограммы прерываний/исключений.

8.10.4 Реализация внешней модульности в программах
Приведем структуру двух программных модулей. Из первого модуля вызывается
подпрограмма суммирования двух 64-битных чисел SUM_64, расположенная во втором
модуле, а сами исходные переменные в виде старшего и младшего слов операндов
находятся в памяти данных первого модуля.
Если глобальное имя объявляется после директивы объявления секции (данных или
кодовой) директивой AREA, то тип глобальной метки по умолчанию является типом
секции, в которой она объявлена и опция типа в директиве EXPORT не нужна. Другими
словами – эта опция может потребоваться только в самом начале программного модуля,
когда ни одна из секций еще не объявлена, а интерфейс модуля с внешним миром
декларируется. Такая же технология распространяется на объявление внешних имен
директивой IMPORT. Если это делается внутри уже объявленной секции, то внешнее имя
автоматически получает тип текущей секции.
Символы в двух программных модулях, объявленные директивой
EXPORT в одном (глобальное, общедоступное имя) и директивой IMPORT в
другом (внешнее имя), должны быть абсолютно идентичны. В процессе
компоновки они будут иметь одинаковые адреса.

152

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
; Модуль №1
; Интерфейс с другими модулями:
; Объявление внешней подпрограммы
IMPORT SUM_64, CODE
; Объявление глобальных переменных
EXPORT W1_L, DATA
EXPORT W1_H, DATA
EXPORT W2_L, DATA
EXPORT W2_H, DATA
;

; Объявление кодовой секции
AREA MyCode, CODE, ReadOnly
;

; Передача параметров в подпр.-му
;

; Вызов подпрограммы
BL SUM_64
;

; Объявление секции данных
AREA MyData, DATA, ReadWrite
W1_L SPACE 4
W1_H SPACE 4
W2_L SPACE 4
W2_H SPACE 4
;

; Конец программного модуля №1
END

; Модуль №2
; Интерфейс с другими модулями:
; Объявление общедоступной подпрограммы
EXPORT SUB_64, CODE
; Объявление внешних переменных
IMPORT W1_L, DATA
IMPORT W1_H, DATA
IMPORT W2_L, DATA
IMPORT W2_H, DATA
;

; Объявление кодовой секции
AREA MyCode, CODE, ReadOnly
;

; Подпрограмма суммирования SUM_64
SUM_64
PROC
; Тело подпрограммы
;

ENDP
; Объявление секции данных
AREA MyVar, DATA, ReadWrite
BUF
SPACE 4*20
;


; Конец программного модуля №2
END

Ассемблер допускает и второй способ определения внешних имен – директивой
EXTERN (внешнее имя). Между этими двумя директивами имеется некоторое различие
(см. табл. 8.2).
Таблица 8.2 Описание директив
Директива

Особенности работы

IMPORT

Внешнее имя всегда включается в таблицу символов. Если внешний символ не был
найден во время компоновки (например, файл с такой меткой или именем
переменной еще не написан или не подключен к проекту) – генерируется сообщение
об ошибке.
Внешнее имя включается в таблицу символов только тогда, когда к нему имеется
обращение из текущего программного модуля. В противном случае – не включается,
и сообщение об ошибке не генерируется. Это удобно, когда программные модули
отлаживаются отдельно. Модуль, на который делается внешняя ссылка, может быть
создан потом.

EXTERN

1. Как изменится начало кодовой секции в модуле № 1 примера, если
объявления общедоступных и внешних имен будут делаться не в начале
файла, а по ходу программы?
2. А как при этом будет выглядеть начало кодовой секции в модуле
№ 2?
3. Как Вы думаете, какую из двух директив нужно использовать, если модуль с
подпрограммой FUNC1 еще не написан, а модуль, из которого вызывается функция
FUNC1, уже имеется и основную программу этого модуля без вызова функции
FUNC1 нужно отладить?
4. Допустимо ли в разных программных модулях иметь кодовые секции с
одинаковыми именами (в примере секции MyCode)?
153

ГЛАВА 8. ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММ С ИСПОЛЬЗОВАНИЕМ
КРОСС-СРЕДСТВ
5.
6.
7.
8.

А могут ли секции данных в разных программных модулях иметь разные имена?
Какой объем буфера данных резервируется в секции данных модуля № 2?
В каких случаях Вы будете пользоваться опцией WEAK («Слабый символ»)?
А можно ли обойтись без шаблонов подпрограмм обслуживания
прерываний/исключений?
1. После объявления кодовой секции, перед использованием
символа SUM-64 (вызовом подпрограммы) нужно объявить его
внешним символом:
; Объявление кодовой секции
AREA MyCode, CODE, ReadOnly
; Объявление внешнего символа
IMPORT SUM_64

2. Естественно, что описанная в этом модуле подпрограмма должна быть объявлена
общедоступной. При этом, как и в предыдущем случае, тип имени будет
автоматически определяться типом секции, в которой находится директива:
; Объявление кодовой секции
AREA MyCode, CODE, ReadOnly
; Объявление общедоступного символа – начала подпрограммы
EXPORT SUM_64
3. Директиву EXTERN.
4. Да, в этом случае обе секции при компоновке будут объединены в одну с общим
именем.
5. Да, как в нашем примере. При компоновке они будут объединяться в одну по
правилам, указанным компоновщику (например, последовательно друг за другом).
6. Арифметическое выражение (4*20) будет вычислено на стадии трансляции с
результатом 80 байт.
7. Например, для описания начальных адресов подпрограмм обслуживания
прерываний/исключений директивой EXPORT, которые пока еще окончательно
не?
разработаны,
а
являются
подпрограммами-шаблонами
(первыми
приближениями).
8. Нет, так как, по крайней мере, начальная часть таблицы векторов
прерываний/исключений должна обязательно присутствовать в проекте. Вы не
должны выполнять ни одну программу без начальной инициализации процессора и
стека – об этом в следующей главе подробно.

Список рекомендуемой литературы
1) Mahout V. Assembly language programming: ARM Cortex-M3. – Wiley-ISTE. – 2012.
2) Hohl W., Hinds C. ARM Assembly language. Fundamentals and Techniques. Second
Edition. – N.-Y.: CRC Press.– 2015. – 453 p.
3) Электронный ресурс http://www.keil.com

154

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ

9 ПЕРВЫЕ ШАГИ В
ПРОГРАММИРОВАНИИ НА
АССЕМБЛЕРЕ
Оглавление
9.1. Структура стартового файла проекта ............................................................................... 155
9.2. Как создать новый проект? ................................................................................................ 158
9.3. Как создать файл с программой на Ассемблере и включить его в проект? ................. 159
9.4. Как подключить к проекту стартовый файл? .................................................................. 161
9.5. Как перейти к редактированию исходного файла? ......................................................... 161
9.6. Как настроить параметры среды μVision? ....................................................................... 162
9.7. Как выполнить трансляцию исходных файлов проекта и сборку проекта? ................. 164
9.8. Как запустить программу на отладку? Приемы отладки. .............................................. 168
9.9. Непосредственная и регистровая адресация операндов ................................................. 170
9.10. Как заменить шаблон обработчика прерывания ........................................................... 174

9.1 Структура стартового файла проекта
Процессор – сложное устройство, не только программноуправляемое с помощью пользовательских программ, но программнонастраиваемое, с помощью дополнительных специальных программ.
Проект не может состоять из одной программы пользователя, в нем
обязательно должна присутствовать программа инициализации процессора
и встроенных в него периферийных устройств (в том числе сопроцессора).
Функцию начальной инициализации берет на себя так называемый стартовый файл
SrartUp.s, который пишется не на языке высокого уровня, а на Ассемблере, так как должен
иметь доступ ко всем системным ресурсам процессора без каких-либо ограничений.
Производители микроконтроллеров, микропроцессоров, а также интегрированных
сред разработки предлагают пользователям свои версии стартовых файлов,
адаптированные не только к типу центрального процессора, но и к встроенной в
процессор и микроконтроллер периферии. Начинающим программистам очень сложно
самостоятельно выбрать тип стартового файла по ряду причин:
1) Низкая начальная квалификация, которая не позволяет понять, что и с какой целью
включено в стартовый файл, что можно добавить или безболезненно исключить из
него.
2) Стартовые файлы используются как своеобразные «черные ящики», особенно
программистами на языках высокого уровня – в них можно «заглядывать», но трогать
не рекомендуется, во избежание возможных проблем.
3) От типа и структуры стартового файла зависит и спектр возможностей, которые будут
поддерживаться при программировании на Ассемблере и на С/С++.

155

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
Перед авторами книги стоял выбор: 1) ничего не объясняя, дать какой-то стартовый
файл в качестве «черного ящика» и приступить к изучению системы команд процессора и
технологии программирования на Ассемблере; 2) попытаться предварительно объяснить
читателю, по крайней мере, назначение отдельных блоков стартового файла, чтобы в
последующем можно было, при необходимости, самостоятельно модифицировать его.
Мы выбрали второй путь, рассчитывая на заинтересованного читателя, который
до всего хочет «докопаться» сам, которого не устраивает принцип: «возьми и пользуйся,
за тебя уже все продумали более квалифицированные специалисты». Именно поэтому мы
рассмотрели раньше такие, казалось бы, очень специфические вопросы, как
секционирование программных модулей, принцип построения таблицы векторов
прерываний/исключений, использование директив процессора для управления
распределением памяти и др. (изложенные, в основном, в предыдущей главе). Теперь,
знакомясь со структурой стартового файла проекта, Вы будете «вооружены», – многое
Вам, если не все в этом файле, должно быть понятно. Если это не так, перечитайте,
пожалуйста, предыдущую главу.
Итак, предлагается версия стартового файла StartUp_1.s, подготовленная авторами
книги специально для начинающих изучать систему команд процессоров ARM Cortex-M и
программирование на Ассемблере в среде μVision. Она содержит подробные
комментарии, описывающие назначение отдельных фрагментов этого файла:
При
работе
с
подпрограммами (функциями), а
также для временного сохранения и
восстановления данных обязательно
нужен стек. Здесь задается размер
стека и объявляется специальная
секция STACK в памяти данных,
которая будет выделена для него
директивой SPACE. Так как стек
заполняется в сторону убывающих адресов, вершиной стека является адрес следующей,
после директивы резервирования памяти, ячейки памяти – _initial_sp.
Один из важнейших
разделов стартового файла –
определение
таблицы
векторов
прерываний/исключений
и
размещение ее в начале
кодовой памяти. Как видите,
первый вектор – это адрес
вершины стека в системе,
второй – точка входа в
обработчик прерывания по
сбросу процессора, а третий –
в обработчик прерывания по
немаскируемому запросу прерывания. Мы ограничились только этими тремя векторами,
при необходимости Вы сможете определить и все остальные векторы
прерываний/исключений. Заметьте, что сначала векторы размещаются в секции памяти
данных по имени Reset и только затем, автоматически, компоновщик переносит их в
начальную область кодовой памяти, как это и требуется в соответствии с архитектурой
ARM-процессоров. Заметьте также, что для размещения адресов точек входа в
подпрограммы (векторов) применяется псевдокоманда резервирования 32-разрядных слов

156

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
в памяти данных DCD. В качестве параметров для нее используются символические имена
точек входа в подпрограммы-обработчики, которые определяются ниже.
Подпрограммы обработчиков
прерываний/ исключений должны
быть размещены в кодовой секции,
которая здесь и объявляется. Это
должны
быть
общедоступные
процедуры, более того, такие,
которые в последующем можно
будет переопределить – опция
WEAK в объявлении глобальной
метки.
В процедуре обработчика по
сбросу процессора должна быть
выполнена его инициализация. В
данной версии файла она не
делается, процессор будет работать
в
режиме
«по
умолчанию».
Единственная операция, которая выполняется в обработчике Reset_Halter в данном случае,
– это передача управления программе пользователя по внешней метке MyProg (это может
быть любое имя, например, main, соответствующее имени Вашей программы). Для этой
цели используется специальная псевдокоманда Ассемблера LDR Rn, =Label, с
особенностями работы которой мы познакомимся уже в этой главе. Фактически, в регистр
r0 загружается адрес точки входа в программу пользователя, а следующей командой
косвенной передачи управления по содержимому регистра r0 (BX r0) этот адрес
записывается в счетчик команд процессора PC, в результате чего и выполняется переход
на программу пользователя.
Именно с команды LDR r0, =MyProg (расположенной в стартовом
файле) будет выполняться любая программа пользователя после сброса
процессора в среде μVision, когда Вы будете отлаживать ее в симуляторе.
Обязательно объявляйте точку входа в свою программу MyProg
общедоступной с помощью директивы Ассемблера EXPORT.
Теперь покажем, на примере
обработчика
немаскируемого
прерывания
NMI,
как
программируется «шаблон» любой
процедуры
обслуживания
прерывания/исключения.
Он
содержит всего лишь команду
зацикливания программы (передачи управления на себя – «B .»). Точка «.» в Ассемблере
означает текущее значение счетчика команд PC в начале выполнения команды, адрес
размещения текущей команды в памяти. Такой обработчик ничего не делает, но ничего и
не портит. Если прерывание NMI возникнет (и разрешено), мы попадем в эту процедуру.
Сам факт попадания важен, так как будет свидетельствовать о том, что запрос прерывания
воспринимается системой. Далее Вы сможете заменить «слабого» обработчика реальным
«сильным», в котором опция WEAK отсутствует.
Осталось только выровнять текущую кодовую секцию по 32-разрядному слову,
чтобы
последующие
коды
программы
пользователя
размещались
в
памяти

157

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
оптимально с точки зрения считывания их на конвейер команд, и завершить текст
программного модуля директивой END – конца ассемблерного текста.
Стартовый файл исключительно важен. В нем, как минимум,
резервируется место под стек системы и передается управление программе
пользователя. Как максимум, выполняется инициализация процессора и
периферии. Если Вы не все поняли в структуре стартового файла, не
огорчайтесь! Мы будем постепенно знакомиться с основами
программирования на Ассемблере и, в том числе, дадим дополнительные объяснения по
работе этого файла. Вы можете найти файл в каталоге проекта CPU_0.

9.2 Как создать новый проект?
Для того, чтобы создать новый проект, обратитесь в меню Project
и выберите из него команду «New μVision Project». Откроется окно
создания проекта «Create New Project», в котором можно
«путешествовать» по дереву каталогов Вашего компьютера с целью
выбора нужного каталога. Если каталог для проекта еще не создан, среда μVision
позволяет сделать это. Не выходя из окна создания проекта, нужно обычными средствами
операционной системы перейти в подходящую директорию и создать в ней новую папку
командой «Создать папку». Естественно, для этого необходимо указать символическое
имя каталога, допустим «Проба» (имена на русском языке допускаются операционной
системой). После этого нужно открыть папку и создать в ней файл нового проекта. Для
этого достаточно ввести только имя проекта, а стандартное расширение будет создано
автоматически. Вы можете использовать для имени проекта то же имя, которое имеет и
папка, его содержащая. Это удобно и исключает путаницу. Осталось выполнить в
текущем окне команду «Сохранить» проект.
После этой команды среда μVision предложит Вам определиться
с целевым устройством, для которого Вы будете писать и отлаживать
программное обеспечение – откроется окно «Select Device for Target
‘Target 1’» («Выберите целевое устройство, ‘Цель 1’»).
Имеется в виду, что в последующем Вы сможете проверить свою
программу не на одном устройстве, а на нескольких, например, сначала
в симуляторе, затем – на оценочной плате, потом на созданном Вами
контроллере. Мы привели фрагмент этого окна, чтобы показать
дальнейшую последовательность действий.
Поддерживаемые средой μVision устройства (процессоры
ARM и микроконтроллеры на их основе) скрыты в списке ARM за
«крестиком» «+». Щелкните на нем мышкой и список раскроется.
Таким же образом выберете целевое устройство, для которого
планируется разработка. Мы изучаем семейство процессорных ядер
на базе ARM Cortex M4. Откройте соответствующую позицию
списка «ARM Cortex M4». Вам предоставляются две возможности –
выбрать процессоры Cortex M4 без интегрированного на кристалл сопроцессора
поддержки вычислений с плавающей точкой («ARMCM4») или с ним («ARMCM4_FP»).
Выбирайте последний вариант («ARMCM4_FP»), так как мы обязательно будем изучать
и работу сопроцессора FPU. Как только выбор сделан, появится дополнительная
информация в окне краткого описания возможностей выбранного процессора. По
предыдущим главам книги они нам уже знакомы: полноценный 32-разрядный процессор,
предназначенный для встраиваемых в оборудование приложений; простая программная
модель; высокая производительность и малое потребление энергии; высокая плотность

158

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
кода; эффективный встроенный контроллер прерываний; совместимость вниз (без команд
использования сопроцессора) с ядром Cortex-M3.
После выбора процессорного ядра Вам будет предложено дополнительно выбрать
окружение реального времени выполнения. Окно менеджера выбора соответствующих
программных продуктов.

Первая
позиция
содержит
компоненты
программного
интерфейса
микроконтроллеров Cortex, которые заметно ускоряют и облегчают программирование на
языке высокого уровня С/С++. Среди них: заголовочные файлы с определениями
регистров периферийных устройств; средства абстрактного доступа к ним; примеры.
Вторая позиция позволяет подключить унифицированные драйверы интерфейсов,
такие как драйвер Ethernet, флэш-памяти и др.
Остальные позиции также предоставляют дополнительные опции программистам
на языке С/С++, начиная от стартовых файлов и кончая поддержкой графики, USBинтерфейсов и прочее. С точки зрения программиста на Ассемблере, тем более,
начинающего, можно опустить все дополнительные возможности, нажав сразу (внизу
окна) клавишу «OK». Наш новый проект будет cоздан и
отображен в окне проектов с именем «Проба» (показана
верхняя часть окна). Для данного проекта указано одно
целевое устройство на базе селектированного нами
процессора Cortex-M4F.
Обратите внимание на то, что пока в нашем проекте есть только
одно целевое устройство Target 1 и – никаких файлов, входящих в
проект. Щелкните мышкой на «крестике» перед именем Target 1. В
окно проектов будет выведено имя «Первой группы исходных
файлов проекта» – «Source Group 1». Обычно в проекте достаточно одной группы
исходных файлов. Однако, в ряде случаев, например, если Вы разрабатываете
программное обеспечение на конкурентной основе несколькими группами
программистов, исходных групп файлов может быть несколько. Кроме того, для каждой
из групп файлов можно устанавливать свои опции (настройки) среды.

9.3 Как создать файл с программой на Ассемблере и
включить его в проект?
Открыть новый файл для написания программы можно несколькими способами:
1) Из меню «Файл» («File»), выбрав команду «New» («Новый»).
2) Щелкнув клавишей мыши на стандартную пиктограмму открытия нового файла –
первая в строке инструментов среды
. Работа с файлами в μVision аналогична
работе с файлами в любой компьютерной программе и поэтому не рассматриватся. Как
видите, используются и стандартные пиктограммы основных действий при работе с
файлами: создать новый файл, открыть имеющийся, сохранить, сохранить
информацию во всех открытых исходных файлах проекта.

159

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
3) Щелкните правой клавишей мыши на пиктограмме группы файлов в исходном коде
(Source Group 1). В большинстве программных продуктов правая клавиша мыши
используется для вызова так называемого контекстно-зависимого меню. Здесь именно
такой случай: Во второй позиции этого меню
имеется команда «Add New Item to Group ‘Source
Group 1» – «Добавить новый объект в группу
‘Группа исходных файлов 1’». Новый объект – это
и есть новый исходный файл – либо на Ассемблере,
либо на СИ. Попутно заметим, что следующая
позиция этого меню «Add Existing Files to Group ‘Source Group 1’» позволяет
добавить (включить) уже существующий файл (который Вы создали ранее или просто
скопировали из другого каталога) в данную группу, т.е. подключить уже имеющийся
файл к проекту.
Таким образом, если Вы воспользовались для создания нового файла обычными
средствами (п.1, 2), то после его сохранения просто подключите файл к проекту. В
последнем случае (п. 3) создание файла выполняется одновременно с его подключением.
При этом на экран выводится окно, в котором
прежде всего нужно выбрать тип создаваемого и
подключаемого файла. Выбор большой: это могут
быть файлы на языке высокого уровня С или С++,
на Ассемблере, заголовочные и т.д. Мы выбираем
исходный файл на Ассемблере. Для этого
достаточно щелкнуть мышью на соответствующей
позиции списка, после чего она будет выделена
подсветкой, и задать имя исходного файла.
Ассемблерные файлы по умолчанию имеют
расширение «.s». После ввода имени файла и
нажатия клавиши «Add» файл сразу будет
подключен к проекту (еще «пустой») и откроется
окно редактирования, в которое можно ввести текст программы.
Как только Вы нажмете на «+» в окне проектов в позиции «Группа исходных
файлов 1» «Source Group 1», содержимое этой
группы раскроется, и Вы увидите, что в ней уже
находится файл My_Prog_1.s.
Теперь
можно
попробовать
написать простую программу, ведь мы
уже находимся во встроенном редакторе
среды. В программе объявляется кодовая
секция MyCode, точка входа программу
MyProg, которая должна быть доступна из
других файлов проекта, и выполняется
лишь одно действие – инициализация
регистра ЦПУ кодом #47. Во избежание
выполнения
в
процессе
отладки
программы
«неопределенного»
кода
программа зацикливается (управление передается на ту же самую команду «B Stop»).
Обратите внимание на то, что в окне редакирования имя программы помечено звездочкой
«*». Это означает, что она еще не прошла этап ассемблирования (трансляции). Как только
это случится, «звездочка» автоматически исчезнет.

160

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ

9.4 Как подключить к проекту стартовый файл?
Мы уже не раз упоминали,
что в любой проект должен быть
включен стартовый файл. Для этого
вызовем еще раз контекстнозависимое меню из окна проектов
(см. выше) и выберем команду
Добавить
файлы
в
группу
«Исходная группа 1» (Add Files to
Group ‘Source Group 1’). Нужно
перейти в каталог, который содержит требуемый файл, указать его тип (в данном случае
исходный файл на языке Ассемблер – Asm Source file (*.s*; *src*; *.a*). Все имеющиеся в
каталоге файлы данного типа будут показаны в окне. Нужно выделить искомый файл
мышью (после этого он будет подсвечен) и нажать клавишу «Добавить» («Add»). Файл
будет подключен к проекту.
Окно проектов изменится: теперь в проекте уже два
файла – с нашей первой программой My_Prog_1.s и
стартовый файл StartUp_1.s. Обратите внимание на то, что к
текущему проекту мы подключили стартовый файл,
расположенный в каталоге CPU_1. Следовательно, к проекту
могут подключаться любые файлы, расположенные в любых
каталогах, их не обязательно копировать в каталог текущего
проекта. Это удобно, так как позволяет постепенно
подключать к проекту те файлы, которые уже прошли стадию отдельного тестирования.

9.5 Как перейти к редактированию исходного файла?
Как только нужные исходные файлы подключены к проекту, их можно
редактировать (модернизировать). Для этого достаточно дважды щелкнуть мышью на
имени файла в окне проектов. В основном окне μVision появится вкладка с текстом
исходного файла.
Так как исходных файлов может быть
несколько,
между
вкладками
(исходными
программами) можно переключаться. Для этого
достаточно щелкнуть мышью на имени вкладки, она станет активной – ее имя будет
подчеркнуто, а вкладка подсвечена желтым цветом. Можете приступать к
редактированию. Технология редактирования исходных файлов похожа на работу в
любом качественном текстовом редакторе и не требует особых пояснений. Единственное
отличие состоит в том, что все синтаксические конструкции языка Ассемблер или С/С++
выделяются цветом для удобства пользователей.
При необходимости, Вы можете перенастроить
редактор в соответствии со своими предпочтениями. В этой
книге используются установки «по умолчанию». Впрочем
есть одно «но». Если Вы хотите, чтобы комментарий в
Ваших программах на русском языке правильно
отображался в исходных файлах и в файлах листинга
(результатах трансляции) войдите в меню «Edit»
(«Редактор») и выберите команду «Configuration»
(«Конфигурация»). Среди множества возможных настроек
выберите опцию «Encoding» («Кодирование») и установите кодировку UTF-8, как
161

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
показано во фрагменте окна опций редактора. Вы сможете видеть комментарии на
русском языке везде, за исключением окна «Дизассемблера» и некоторых других окон
среды μVision, где эти символы не отображаются.

9.6 Как настроить параметры среды μVision?
Прежде чем запустить файлы проекта на трансляцию/компиляцию и последующую
отладку требуется настройка среды. В общем случае – это тонкая работа, которая
позволяет настроить все инструменты среды (транслятор, компоновщик, отладчик и др.)
на нужный пользователю режим работы. При программировании на Ассемблере
настройка заметно упрощается, и по большей части достаточно будет опций «по
умолчанию». По мере приобретения навыков работы со средой μVision Вы сможете
самостоятельно менять настройки, адаптируя их к особенностям своих проектов.
Поэтому, поступим так: дадим лишь краткие рекомендации по настройке для начинающих.
Установить опции для целевого
устройства можно из меню Проекта:
«Project» – «Options for Target ‘Target
1’…» или просто щелкнув мышью на
пиктограмме этой команды
в строке
инструментов. Откроется окно, в котором
имеются
несколько
«вкладок»
для
настройки опций типа процессора Device
(мы это уже делали при создании нового
проекта), целевой платы Target, выходных
файлов Output, файла листинга Listing,
пользовательских настроек User, опций
компилятора С/С++, Ассемблера ASM,
компоновщика Linker, отладчика Debug, дополнительных утилит Utilities. Перед
отладкой любого проекта настройка среды uVision – обязательна. Дальше, по мере
работы над проектом, добавления в него новых файлов и их отладки, значения
установленных Вами опций будут сохранены – их повторного ввода не потребуется.
Советуем на первом этапе установить все опции «по умолчанию» «Defaults» и сверить с
приведенными ниже краткими советами и рекомендациями.

162

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
Таблица 9.1 Рекомендации по настройке опций
Вкладка
Device
Target

Output

Listing

User

C/C++
ASM

Рекомендации по настройке для программистов на Ассемблере
Оставьте тип устройства, который Вы уже выбрали, создавая новый проект.
«По умолчанию». При отладке в симуляторе точной конфигурации памяти конкретного
устройства не требуется. Можно принять диапазон встроенной памяти программ IROM
от 0x до 0x80000 (0.5 Гб), так как в открытой версии среды код все равно
ограничивается объемом 32 Кб. Объема встроенной памяти данных IRAM 0x20000 (131
Кб) начиная с адреса 0x20000000 также вполне достаточно. Обе области кодовой памяти
и памяти данных соответствуют унифицированной карте памяти процессоров ARM.
Остальные опции нужны только при использовании реальной аппаратуры или при
программировании на С/С++.
Достаточно разрешить создание
выходного исполняемого файла с
именем текущего проекта «Create
Executable», генерацию отладочной
информации «Debug Information» и
информации для просмотрщиков
«Browse Information»:
Разрешите формирование файла
листинга «Assemgler Listing» с
расширением
«.lst»,
установив
максимальную длину строки «Page
Width» в количестве 132-х символов,
чтобы
комментарии
могли
разместиться в строке, а также
формирование файла результатов
компоновки «Linker Listing» с
расширением «.map», который будет
содержать карту памяти «Memory
Map», информацию о символах
«Symbols» и перекрестных ссылках
«Cross Reference»
«По умолчанию». Используются только при программировании на С/С++. Можно
дополнительно включить опцию выдачи звукового сигнала при завершении
трансляции или построения проекта «Beep When Complete». Если включить опцию
старта отладки «Start Debugging», то сразу после построения или перестроения проекта
автоматически запускается режим отладки.
«По умолчанию». Использовать при программировании на С/С++.
«По умолчанию». Не устанавливать
опцию Thumb, так как в процессорах
Cortex-M3/M4 используется единый
набор команд Thumb-2, и выбор
между генерацией 16- или 32разрядных
команд
делается
транслятором
автоматически.
Опции раздельных областей памяти
(«Independent») и памяти только для
выполнения («Execute-only Code») не
устанавливать, так как кодовая
память в процессорах Cortex-M3/M4
будет хранить как коды, так и
данные.

163

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
Linker

«По умолчанию». Начальные адреса
областей памяти «Только для чтения»
R/O Base (0x00000000) и «Для чтения и
записи» R/W Base (0x20000000)
соответствуют расположению ПЗУ и
ОЗУ в унифицированной карте
памяти процессоров ARM. Коррекция
не требуется.

Debug

Выберите отладку программы в
симуляторе «Use Simulator» и
разрешите устанавливать точки
останова «Breakpoints», работу с
окнами
переменных
«Watch
Windows», вывод на дисплей
содержимого
памяти
«Memory
Display», работу с инструментами
«Toolbox» и просмотр состояния
системы
«System
Viewer».
Разрешите
автоматическую
загрузку приложения при старте
«Load Application at Startup» и
выполнение программы
main()
«Run to main()».
«По умолчанию». На начальном этапе утилиты не используются.

Utilites

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

9.7 Как выполнить трансляцию исходных файлов проекта и
сборку проекта?
Для этого можно воспользоваться меню «Проект» («Project») или (что
значительно проще) пиктограммами на панели инструментов среды, которые доступны
простым «щелчком» мыши:
. Первая пиктограмма запускает процесс
«ассемблирования», «трансляции» («Translate») текущего исходного файла на языке
Ассемблер или «компилирования» («Compilate») исходного файла на языке С/С++
(выбор делается автоматически по расширению файла). Вторая пиктограмма запускает
процесс «сборки» («компоновки») уже оттранслированных/откомпилированных файлов
проекта – создает выходные файлы проекта для загрузки и отладки: «Build» («Построить
проект»).
Третья

выполняет
последовательно
оба
действия:
трансляцию/компилирование всех исходных файлов и новую сборку проекта – «Rebuild»
(«Перестроить проект»).
На первом этапе (трансляция) создаются выходные файлы в перемещаемом
объектном коде, а на втором (сборка) – в исполняемом объектном коде, которые могут
загружаться на выполнение и отладку в симулятор или в целую плату. Четвертая
пикторгамма предназначена для пакетной обработки исходных файлов (если проект

164

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
большой и сложный), а последняя, пятая, – для остановки (прерывания) запущенного
процесса трансляции/компоновки.
Переключаясь между исходными файлами проекта и запуская каждый из них на
трансляцию, Вы можете убедиться в том, что, с точки зрения синтаксиса языка
программирования, Ваши программы правильные. Пример вывода результата трансляции
программы My_Prog_1.s в окно «Build Output» («Окно вывода результатов построения
проекта») показан ниже. Он свидетельствует о том, что синтаксических ошибок в нашей
первой программе нет.
Если будет обнаружена какая-либо ошибка, то сообщение о ней будет обязательно
выведено в окно результатов трансляции.

1.
Измените в исходной программе имя регистра r0 на имя r20.
Оттранслируйте программу. В чем ошибка?
2.
Щелкните мышью на коде ошибки. Вы автоматичеки попадете на
строку
в
исходной
программе, в которой она
обнаружена. Голубой треугольник являтся
маркером строки с синтаксической ошибкой. Можете приступать к редактированию
исходного файла для устранения ошибки. Выполните повторную трансляцию.
Убедитесь, что ошибка исчезла. А теперь замените команду MOV на команду LDR r0,
#47. Опять ошибка. Что она означает?
3. Убедитесь, что при трансляции стартового файла StartUp_1 ошибок нет.
1.
Сообщение об ошибке: свидетельствует о неправильном
использовании имени регистра. В действительности регистра с именем r20
нет среди регистров ЦПУ (только r0-r15).
2. Сообщение об ошибке
говорит о том, что мы использовали невыполнимый оператор, то есть команду,
которой нет в системе команд процессора. Это действительно так. Дальше мы
покажем, как правильно выполнять загрузку регистров процессора данными.
3. Да. Это так.
Теперь можно изучить файлы с результатами трансляции. Для этого прямо из
среды μVision вызовем команду открыть файл (можно щелкнуть на пиктограмме
клавишей мыши) и перейдем в подкаталог текущего проекта, который содержит файлы
листингов «Listings», укажем
типы интересующих нас файлов
(*.lst), и все файлы с таким
расширением будут выведены в
окно. Выберем нужный (он
будет автоматически подсвечен)
и нажмем кнопку «Открыть». В
окне среды μVision появится
дополнительная
вкладка
с
содержимым файла листинга.
Он содержит с правой стороны
копию программы на языке
165

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
Ассемблера, а с левой – результаты ее трансляции в машинный код: номер строки, адрес
размещения данных (в HEX-коде) и код сгенерированной машинной инструкции.
В нашей программе всего две
команды: первая (MOV) является 32разрядной, а вторая (B) – 16разрядной.
Транслятор
автоматически делает оптимальный
выбор
между
командами
ARM/THUMB
унифицированного
единого набора команд Thumb-2,
создавая
команду
наименьшей
возможной длины.
Обратите внимание на то, что
директивы Ассемблера в машинные коды не транслируются, так как являются командами
для транслятора, а не для процессора. Все адреса в файлах листинга являются
относительными – отсчитываются от начального (нулевого) адреса текущей секции.
Исследуйте
файл
листинга
стартового
файла.
Вначале с помощью директивы
EQU (Эквивалентно) мы определили символическое имя переменной Stack_Size,
задающей требуемый нам размер стека. Так как эта переменная объявлена только для
транслятора, под нее не выделена память, хотя введенное нами значение отобразилось в
столбце машинных кодов (3-я строка). Это свидетельствут о том, что транслятор
воспринял эту переменную как константу и присвоил ей указанное нами значение.
Дальше мы объявили
секцию
данных
и
зарезервировали в ней 1Кбайт
памяти с помощью директивы SPACE и преварительно определенного символического
имени Stack_Size. Как видите, счетчик текущего адреса в секции данных автоматически
получил нужное приращение (0x400), а следующая за ней метка вершины стека _initial_sp
– относительное значение 0x400.
Так как выполнена только трансляция исходных файлов, а компоновка еще не
выполнена, то фактические адреса размещения подпрограмм в файле листинга пока
заменяются нулями, но место под них обязательно выделяется.
С каждой директивой
DCD
резервирования
в
секции
сброса
(RESET)
очередного вектора, счетчик
адреса расположения данных
в
текущей
секции
инкрементируется на 4. Это
позволяет
транслятору
точно
вычислить
размер
таблицы
векторов
прерываний/исключений – 0x0C (переменная _Vectors_ Size). Напомним, что все
содержимое этой секции будет автоматически размещено компоновщиком в начальной
области кодовой памяти (с нулевого адреса). Дальше в стартовом файле определяется
кодовая секция |.Text|, в которой располагаются процедуры обработчиков
прерываний/исключений.

166

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
Первая процедура –
обработчик
сброса
процессора
только
передает
управление
программе пользователя.
Для этого использована
специальная псевдокоманда Ассемблера «LDR r0, =MyProg», которая загрузит
внешний адрес начала пользовательской программы в регистр r0. С помощью команд
косвенной передачи управления по содержимому этого регистра (BX r0) мы окажемся в
начальной точке нашего приложения. Именно с этого места будет выполняться программа
после компоновки приложения и запуска отладчика.
Итак, оба исходных файла проекта прошли этап трансляции. Можно выполнить
компоновку проекта – построить проект. Нажмите клавишу
«Build» («Построить
проект» (на панели инструментов и обратите внимание на сообщение в окне вывода
«Build Output», которое говорит о том, что при создании выходного объектного файла
ошибок и предупреждений не было.
В окне выводится также
краткая
информация
об
использованной в проекте памяти.
Она означает следующее: объем в
байтах: всего кода проекта Code=20; памяти только для чтения RO – data=12,
неинициализированной памяти данных для чтения и записи RW-data=0;
инициализированной нулями памяти данных ZI-data=1024.
В результате компоновки создается не только выходной объектный
файл, который можно загрузить на выполнение или отладку, но и файл карты
загрузки «.map» проекта. Можете, для начала, пропустить специфическую
информацию о том, что находится в этом файле и вернуться к ней при
необходимости. Прежде всего в файле «.map» Вы сможете найти информацию о
локальных и глобальных символах Вашего проекта, их значении (Value), типе (Type),
занимаемом объеме памяти в байтах (Size)и об объекте (секции), в котором они
определены. Рассмотрим, для примера, таблицу глобальных символов нашего проекта.
Среди них символ _Vectors, имеющий значение 0x0, тип Data и размер 4 байта (1 слово),
расположенный в секции RESET.
Действительно, таблица векторов прерываний/исключений и начального адреса
стека должна начинаться с нулевого адреса кодовой памяти.
Символы
Reset_Handler и NMI
Handler – начальные
адреса (0xd и 0x11)
соответствующих
подпрограмм-обработчиков прерываний/исключений, занимающих в памяти 4 и 2 байта
соответственно. Оба символа относятся к программному коду (тип – Thumb Code) и
расположены в кодовой секции |.text|. Последний символ MyProg – начальный адрес
приложения пользователя 0x19.
В этом же файле находится карта памяти проекта. Прежде всего отметим, что в
любом проекте имеется точка входа в проект (Image Entry point), с которой начинается
отладка проекта. В нашем случае – это процедура обслуживания прерывания по сбросу
процессора (расположена по адресу Reset_Handler = 0xd), которая должна выполнить его
инициализацию и передать управление приложению пользователя.

167

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
В карте памяти используются следующие обозначения:
• Load Region LR_1 – область загрузки;
• Execution Region ER_RO – область выполнения «Только для чтения»;
• Execution Region ER_RW – область выполнения «Для чтения и записи»;
• Execution Region ER_ZI – область выполнения «Для чтения и записи»,
инициализированная нулями.
Первые
две
области относятся к
кодовой памяти (ПЗУ), а
остальные – к памяти
данных (ОЗУ). Область
загрузки
может
содержать не только код
программы,
но
и
дополнительные данные,
например
таблицы
констант,
которыми
должна
быть
проинициализирована
определенная часть памяти данных (область инициализированных переменных). В нашем
случае весь код попадает в область выполнения: это таблица векторов
прерываний/исключений; код обработчиков прерываний/исключений; собственно
программа пользователя. Область памяти данных для чтения и записи ER_RW пока нами
не использована, а область памяти для чтения и записи, инициализированная нулями
ER_IZ, – использована для размещения стека.

9.8 Как запустить программу на отладку? Приемы отладки.
Проект создан: все исходные программы прошли стадию
трансляции и компоновки. Можно запустить процесс отладки: из
меню «Debug» («Отладка») или щелкнув мышью в строке
инструментов на пиктограмме
«Start/Stop Debug Session»
«Старт/Стоп отладочной сессии».
Ранее мы показали, как установить опции целевого устройства, чтобы процесс
отладки выполнялся в симуляторе. В этом случае, после запуска отладочной сессии код
проекта будет загружен в программу-симулятор, и управление кодом будет передано
отладчику (автоматически появится и строка инструментов отладчика).
В процессе загрузки кода будет автоматически сформирована таблица векторов
прерываний/исключений (в нашем случае ее начальная часть), к которой процессор
обращается в самом начале своей работы, в частности для инициализации указателя стека
SP. После этого будет вызван обработчик прерывания по сбросу процессора
Reset_Handler. Его начальный адрес и является точкой входа в проект.
Так как обработчик состояния
сброса процессора находится в
стартовом
файле,
он
будет
активизирован (открыт), и курсор текущей команды, подлежащей выполнению (синий и
желтый треугольнички), будет установлен на начальной команде обработчика.
Можно открыть и окно Дизассемблера из меню View (Просмотр). В этом окне
курсор (желтая стрелка) также будет показывать на команду, подлежащую выполнению.

168

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
Однако, в отличие от окна программы в исходных кодах, это будет уже не псевдокоманда,
а реальная, созданная транслятором, команда процессора:
Таким образом, точка входа в процесс отладки – это начало процедуры обработки
прерывания по сбросу процессора Reset_Handler (0xC). Обязательно откройте окно
текущего содержимого регистров центрального процессора из меню View (Просмотр)
или щелкнув на пиктограмме этой команды в строке инструментов отладчика
.
Именно в этом окне мы будем отслеживать ход выполнения
программы. Отметим, что текущее содержимое регистров ЦПУ
автоматически отображается в шестнадцатеричной системе
счисления. Для раскрытия/сокрытия содержимого определенной
группы регистров достаточно шелкнуть мышью на соответствующем
прямоугольничке (символ «+» – регистры скрыты; «-» – открыты. В
нашем примере открыто содержимое регистров процессорного ядра и
внутренних регистров процессора, отвечающих за его режим работы.
Вы видите, что указатель стека SP уже проинициализирован
значением вершины стека 0x20000400, счетчик команд PC –
начальным адресом обработчика Reset_Handler, а сам процессор
находится в потоковом режиме работы Thread с привилегированным
доступом ко всем ресурсам Privileged, что и должно быть по
умолчанию. Бит, установленный в регистре состояния программы PSR
– это тот, который отвечает за текущий набор команд (ARM или
Thumb), с которым в данный момент работает процессор. Так как все
процессоры Corteх-M всегда работают в одном и том же режиме Thumb-2 с
автоматической генераций наиболее компактного кода, то по умолчанию установлен
именно бит Thumb.
Теперь можно обратиться к командам управления отладкой, которые находятся в
соответствующем меню, начальный фрагмент которого показан ниже, или к панели
инструментов среды μVision
, которые более удобны, так
как позволяют запустить нужную команду отладчика простым щелчком
мыши. Первая команда меню запускает/останавливает отладочную
сессию, вторая – вызывает сброс процессора, то есть переход на
обработчик прерывания Reset_Handler, третья – позволяет запустить
программу в режиме непрерывного выполнения (прогона - RUN), четвертая –
остановить процесс непрерывного выполнения, пятая, который Вы будете пользоваться
наиболее часто на этапе изучения команд процессора, – выполнить одну текущую
команду – сделать шаг. Это так называемый режим пошагового выполнения программы,
который наиболее удобен для начинающих. С остальными возможностями отладчика и
его командами будем знакомиться постепенно.
Выполним нашу программу в пошаговом режиме работы. Сделаем один шаг.
Курсор текущей команды, подлежащей выполнению, в окне с исходным кодом
программы переместится, указывая на следующую команду, а уже выполненная команда
будет
отмечена
слева
зеленым прямоугольником
(признаком выполнения).
Так как эта псевдокоманда должна загрузить в регистр r0 адрес точки входа в
программу приложения, обратим внимание на изменение
содержимого регистра r0 в окне регистров. Оно изменилось
(0x19), а факт изменения отмечен в окне синим фоном. Точно так
169

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
же изменилось и состояние счетчика команд PC, который теперь указывает на адрес
очередной команды, подлежащей выполнению
.
В окне Дизассемблера также будет виден адрес расположения этой команды.
Сделаем еще один шаг. Мы должны
перейти к программе пользователя. Среда
μVision автоматически активизирует окно этой
программы (имя файла выделяется зеленым
фоном и подчеркиванием) и устанавливает
курсор слева от команды, подлежащей
выполнению. Это первая команда нашей
простой программы MOV r0,#47. Если Вы
обратитесь к окну Дизассемблера, то увидите,
что курсор подлежащей выполнению команды
также переместился и показывает на ту же команду.
Однако, обратите внимание на некоторое несоответствие. Адрес этой команды –не
число, загруженное ранее в регистр r0 (0x19), а на единицу меньше. Дело в том, что
раньше в процессорах ARM младшим битом в команде перехода устанавливался нужный
набор команд процессора (0- ARM, 1- Thumb), в котором процессор должен работать
после завершения перехода. Так как в процессорах Cortex-M переключений из одного
набора команд в другой нет и процессор всегда работает в режиме Thumb-2, то для
сохранения совместимости с предыдущими версиями ARM-процессоров адрес перехода
всегда устанавливается на 1 больше, чем фактический адрес. В процессе выполнения
команды перехода (в данном случае команды косвенной передачи управления BX r0 по
адресу в регистре r0) «лишний» младший бит автоматически обнуляется, и переход
выполняется на правильный адрес, о чем свидетельствует и новое содержимое счетчика
команад в окне регистров.
Сделаем очередной шаг и увидим
результат выполнения нашей программы
– содержимое регистра r0 изменится и
будет равно 0x2F=47d, как и требовалось.
При очередном шаге выполняется команда передачи управления на эту же команду.
Процесс выполнения программы зацикливается. Это сделано для того, чтобы в режиме
прогона вы не попали в ситуацию выполнения «мусорного» (случайного) кода.
Содержимое счетчика команд не меняется, и процессор все время выполняет одну
и ту же команду перехода на саму себя. Обратите внимание на то, что обе команды нашей
программы помечены зеленым прямоугольником – значит выполнены.

9.9 Непосредственная и регистровая адресация операндов
Вычислительная мощность любого процессора зависит от
имеющейся в его распоряжении системы команд, а она строится на базе
принятых и реализованных разработчиками процессора способов
адресации операндов. Под способом адресации операнда понимается
механизм кодирования операнда в формате команды. Самым простым
способом адресации является непосредственная адресация, когда в
формате команды часть разрядов отводится для представления кода операции, а часть –
для представления собственно операнда-константы:

170

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
Команда
Поле кода операции

Поле непосредственного операнда-константы

Этот метод имеет естественные ограничения. В ARM-процессорах любая команда
должна иметь фиксированный формат – 32 разряда. Процессоры Cortex-M допускают как
32-разрядный, так и 16-разрядный формат команд. Следовательно, непосредственный
операнд не может занимать всю разрядную сетку команды и должен размещаться в поле
меньшей разрядности (например, в младшем полуслове для 32-разрядной команды).
Так как большинство операций в ARM-процессорах выполняются над содержимым
регистров, естественной является регистровая адресация операндов, когда в формате
команды указывается не сам операнд, а адрес (внутренний код) регистра ЦПУ, в котором
находится операнд:
Команда
Поле кода операции

Код регистра Rd

Код регистра Rn

Код регистра Rm

Мы имеем 16 регистров общего назначения ЦПУ r0-r15. Следовательно, для
кодирования всех регистров ЦПУ (младшего и старшего банков) достаточно всего 4-х
разрядов, а для кодирования регистров только младшего банка r0-r7 – всего 3-х разрядов.
Напомним, что в общем случае команды процессора могут быть 3-операндные и 2операндные. В первом случае поле в формате команды, используемое для кодирования
двух регистров-источников и одного регистра приемника, имеет длину 3*4=12 бит, а во
втором – 2*4=8 бит. Эти цифры уменьшаются при работе только с регистрами младшего
банка (3*3=9 бит и 2*3=6 бит). Система команд процессоров ARM Cortex-M устроена так,
что при работе с регистрами младшего банка в основном генерируются более компактные
16-разрядные команды набора Thumb, а при использовании хотя бы одного регистра из
старшего регистрового банка, – 32-разрядные команды набора ARM.
Рекомендации по работе с исходной программой при ее
модернизации:
1) Вы сколько угодно раз можете редактировать исходную программу,
ассемблировать ее, компоновать, строить проект и запускать его на
отладку. Имейте только в виду, что на диске при сохранении файлов останется только
последняя версия Вашей программы, а все предыдущие будут потеряны.
2) Если Вы желаете сохранить исходную версию программы, поступите так: находясь в
окне редактирования программы, выберите из меню «Файл» («File») команду
«Сохранить как» («Save as») и сохраните текущий файл под новым именем,
например, My_Prog_2.s. Вкладка с содержимым нового файла автоматически появится
на экране. Можете приступать к редактированию.
3) Однако, к текущему проекту будет все еще подключен исходный файл My_Prog_1.
Именно он будет транслироваться и компоноваться с остальными файлами проекта до
тех пор, пока Вы не отсоедините его от проекта. Сделать это можно так: в окне
проекта выделите файл, подлежащий отключению, щелкните правой клавишей мыши
на его имени и из выпадающего контекстного меню
выберите команду «Отключить файл» («Remove
File ‘My_Prog_1.s»). Он будет отключен от
текущего проекта (увидите в окне проекта), но
останется на диске в каталоге проекта и может быть
при необходиомости подключен вновь.

171

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
4) После завершения редактирования нового файла и сохранения на диске, подключите
его к проекту из контекстного меню исходной группы файлов «Source Group 1». Теперь
можно выполнять трансляцию нового файла, просмотр листинга, построение и отладку
всего проекта с новым исходным файлом.
5) Любой уже ненужный файл можно закрыть, щелкнув на его имени правой клавишей
мыши. В появившемся контекстно-зависимом меню нужно выбрать команду
«Закрыть» («Close»).
6) Если Вы просматривали файл листинга, а затем модернизировали исходный текст и
перетранслировали программу, среда μVision уведомит Вас о изменении в этом файле:
Файл был изменен извне. Перевывести? В
случае Вашего согласия новая версия файла
будет выведена на экран.
В задании ниже попробуйте разные варианты работы с исходными файлами. Для
вышего удобства мы сохранили все модифицированные версии файлов (отличаются
цифрами в конце имени файла). Если не справитесь сами, – можете подключить к проекту
готовые файлы и поработать с ними.

2)

3)
4)

5)

1) Модернизируйте программу My_Prog_1 так, чтобы выполнялась
последовательная загрузка в один и тот же регистр r3 нескольких
непосредственных констант: 47, 48, 49, 50. Оттранслируйте ее. Откройте
файл листинга, проанализируйте коды сгенерированных команд и выделите
в них битовые поля, которые отводятся для размещения непосредственных
операндов. Создайте выходные файлы проекта (постройте проект) и выполните его
отладку.
Теперь сделайте последовательную загрузку этих же непосредственных констант в
разные регистры ЦПУ r0, r1, r2, r3. Выполните трансляцию. Найдите в кодах
команд поля, которые содержат адреса регистров-приемников данных.
Загрузите те же операнды в регистры старшего регистрового банка r9, r10, r11, r12.
Изменилать ли технология адресации регистров-приемников?
Еще раз измените программу: загрузите регистры r0, r1 кодами 47, 48. Рассчитайте
сумму содержимого этих регистров с записью результата в регистр r0 (ADD r0,r1),
затем – сумму текущего содержимого r0 и r1 с записью результата в регистр r7
(ADD r7, r0, r1). Просмотрите файл листинга. Почему эти две команды ADD имеют
разный формат?
Загрузите программу на отладку. Результат сложения соответствует ожидаемому?

1) Фрагмент файла листинга My_Prog_2.lst показывает, что
генерируются 32-разрядные команды, в которых в старшем полуслове
находится код операции, а в младшем – непосредственные данные. Они занимают
младший байт младшего полуслова (разряды 7-0). Старший байт младшего
полуслова (пока без детальных комментариев) содержит поле адреса регистраназначения (младший нибл) – число 3.

172

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
2) Результат трансляции файла My_Prog_3.s выглядит так: старшее полуслово не
изменилось (код операции), а младшее
по-прежнему содержит в младшем
байте непосредственные операнды, а в
старшем байте – коды (адреса)
регистров-приемников, соответствующие их номеру. Таким образом, в команде
MOV одновременно используются два способа адресации – непосредственная
операнда-источника и регистровая – операнда-приемника.
3) Файл
листинга
My_Prog_4.lst
подтверждает, что в 4-битовом поле
команды (младший нибл второго
байта) действительно находится
адрес регистра-приемника данных, соответствующий его номеру. Следовательно,
технология регистровой адресации операндов распространяется на весь
регистровый банк процессора.
4) Из файла листинга My_Prog_5.lst следует, что первую двухоперандную команду
сложения
транслятору
удалось закодировать более
компактным
16-разрядным
кодом Thumb – под поле
адресов регистра-источника и
приемника отведено только 2*3=6 бит. Вторая 3-операндная команда сложения
является уже 32-разрядной командой набора ARM и в ней имеются три 4-битовых
поля адресов первого и второго операнда-источника и операнда-приемника.
Можете поэкспериментировать самостоятельно, чтобы определить, где в команде
реально находятся эти поля.
5) Да. Первое сложение должно дать результат 0x2F+0x30=5F, что
соответствует содержимому региcтра r0. Второй командой к
этому значению добавляется число в регистре r1 (0x30) и
результат (0x5F+0x30=0x8F) сохраняется в регистре r7:
Мы познакомились с двумя типовыми способами адресации
операндов: непосредственной и регистровой. Поняли, что транслятор в
зависимости от типа операции и используемых регистров выбирает наиболее
экономичный способ кодирования команд, используя при этом как 16разрядные, так и 32-разрядные команды унифицированного набора команд
Thumb-2. При этом никаких дополнительных директив переключения между наборами
команд Ассемблеру давать не нужно.
Даже из простых примеров видно, что технология кодирования команд
процессоров Cortex-M достаточно сложна, особенно в том случае, когда выполняются
попутные операции сдвига и требуется закодировать не только адрес регистра-источника,
но и тип сдвига операнда и число разрядов сдвига. Поэтому, далее в книге мы будем
полностью доверять транслятору и проверять работу программ «по факту», т.е. по
результату, не вдаваясь детально в особенности размещения данных в отдельных полях
команды.
Очень надеемся, что столь подробное изложение основ работы в интегрированной
среде разработки μVision в этой главе не охладит пыл читателя в изучении системы
команд процессора и методов эффективного программирования практических задач. В
следующих главах будем полностью опираться на приобретенный здесь опыт.
173

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ

9.10 Как заменить шаблон обработчика
прерывания/исключения реальной процедурой?
В этой главе мы рассмотрели структуру стартового файла, пригодную для
разработчиков программного обеспечения на Ассемблере. В его основе лежит таблица
векторов прерываний/исключений, которая должна быть размещена в начале кодовой
секции перед выполнением любой программы пользователя. Осталось неясно, как
процедуру «шаблон» заменить реальной процедурой. Покажем это на примере процедуры
обслуживания немаскируемого прерывания NMI.
Прежде всего на основе
исходного
файла
StartUp1.s
создадим новый файл с нужной нам
процедурой
обслуживания
прерывания NMI.s. Проще всего это сделать с
использованием команды «Сохранить как» с
последующим редактированием копии файла.
Оставим в файле только кодовую секцию |.text|,
которая предназначена как раз для размещения
процедур обработчиков прерываний/исключений. Добавим в текст (для иллюстрации
изменений) две команды NOP (пустые операции). В отличие от процедуры шаблона в
директиве объявления глобальной метки NMI_Handler, уберем опцию [WEAK], то есть
сделаем эту метку и помеченную ею процедуру «сильной», чтобы на стадии сборки всего
проекта она могла бы автоматически заменить «процедуру-шаблон».
Прежде, чем выполнить трансляцию нового файла, его нужно
подключить к проекту. Сделаем это. Теперь в составе проекта
«Проба» уже три файла: заимствованный нами из каталога CPU_1
стартовый файл, который, в том числе, содержит «процедурушаблон», файл с программой пользователя и только что
подключенный к проекту файл NMI.s с реальной процедурой
обслуживания немаскируемого прерывания.
Выполним
трансляцию
нового файла и просмотрим файл
листинга.
Действительно,
процедура
обслуживания
прерывания NMI изменилась (в
нее включены две команды
NOP).
При этом результаты
трансляции двух
остальных
файлов,
естественно,
не
меняются. Изменения можно будет увидеть только после компоновки проекта и загрузки
его на отладку. В процессе компоновки «процедура-шаблон» будет автоматически
заменена реальной процедурой.

174

ГЛАВА 9. ПЕРВЫЕ ШАГИ В ПРОГРАММИРОВАНИИ НА АССЕМБЛЕРЕ
Но увидеть это можно будет только в файле карты загрузки проекта проба.map, из
которого следует, что новая процедура имеет адрес загрузки 0x19 и занимает шесть байт в
памяти (как и должно быть в соответствии с файлом листинга).

Мы уже отмечали, что адрес перехода, загружаемый в счетчик команд, должен
быть на 1 больше фактического адреса размещения процедуры. Это бит,
подтверждающий, что после перехода режим выполнения процессором набора команд
Thumb-2 должен быть сохранен. Он будет автоматически сброшен при выполнении
перехода. Именно поэтому в таблице символов точка входа в процедуру NMI имеет
значение 0x19, а не 0x18.
Таким образом, Ассемблер ARMASM допускает отладку любых программ с
процедурами-шаблонами обработчиков прерываний/исключений. Как только реальные
обработчики понадобятся – их можно будет легко подключить к проекту.

Список рекомендуемой литературы
1) Mahout V. Assembly language programming: ARM Cortex-M3. – Wiley-ISTE. – 2012.
2) Hohl W., Hinds C. ARM Assembly language. Fundamentals and Techniques. Second
Edition. – N.-Y.: CRC Press.– 2015. – 453 p.
3) Электронный ресурс http://www.keil.com

175

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ

10 ДОСТУП К ДАННЫМ В
РЕГИСТАХ ЦПУ И ПАМЯТИ
Оглавление
10.1. Загрузка регистров ЦПУ. Краткая справка .................................................... 176
10.2. Команды пересылки данных ............................................................................ 178
10.3. Загрузка 32-разрядных констант ..................................................................... 181
10.4. Директива принудительного размещения литеральных пулов .................... 184
10.5. Загрузка данных из кодовой памяти по адресам размещения данных ....... 185
10.6. Косвенная адресация операндов в памяти...................................................... 187
10.6.1. Общие положения ...................................................................................... 187
10.6.2. Псевдокоманда загрузки адреса данных в регистр указатель ............... 187
10.6.3. Доступ к данным с использованием косвенной адресации.................... 188
10.7. Команды работы с памятью ............................................................................. 191
10.7.1. Какие данные используются в операциях загрузки/сохранения?.......... 191
10.7.2. Форматы команд загрузки/сохранения данных....................................... 192
10.7.3. Базовая адресация....................................................................................... 193
10.7.4. Базовая адресация со смещением ............................................................. 196
10.8. Загрузка и сохранение двойных слов .............................................................. 198
10.9. Технология организации циклов при работе с массивами данных .............. 198
10.10. Разработка подпрограмм копирования данных в памяти ........................... 202
10.11. Как открыть окно содержимого памяти? ...................................................... 204
10.12. Как управлять форматом выдачи данных в окно памяти при отладке? .... 207

10.1 Загрузка регистров ЦПУ. Краткая справка
В большинстве случаев программисты стараются полностью
использовать разрядность процессора – работают с 32-разрядными
словами, загружая их в 32-разрядные регистры ЦПУ на обработку.
Возможна работа и с более короткими данными – полусловами и байтами.
В последнем случае процессор будет автоматически выполнять
преобразование более коротких данных в «длинные» 32-разрядные слова.
В зависимости от типа команды и использованных в ней операндов транслятор может
генерировать либо 16-, либо 32-разрядные команды унифицированного набора команд
Thumb-2.
Краткий список наиболее употребительных команд и псевдокоманд загрузки
регистров ЦПУ представлен ниже – табл. 10.1. Все они рассматриваются далее более
подробно.

176

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Таблица 10.1 Наиболее употребительные команды и псевдокоманды загрузки регистров
Синтаксис

Способ
адресации
источника

операнда-

Описание

Пересылка из регистра в
регистр
Загрузка константы
Пересылка из регистра с
предварительной обработкой в
сдвиговом регистре
(Вырабатывает флаги N, Z!)
Псевдокоманда:
Непосредственная – по содержимому Непосредственная адресация, с
загрузить адрес по метке
счетчика команд PC и
расчетом адреса в памяти по
ADR Rd, Label
непосредственному смещению
текущему содержимому
Ассемблер автоматически
счетчика команд и
рассчитывает величину смещения до непосредственному смещению
метки и генерирует команду
до метки Label
ADR Rd, (PC, #offset)
Rd ← ((PC) + #offset)
LDR Rd, [Rn]
Косвенная
Косвенная адресация по
Фактически генерируется команда
текущему содержимому
базовой адресации с нулевым
регистра-указателя Rn
непосредственным смещением:
(базового регистра)
LDR Rd, [Rn, #0]
Rd ← [(Rn)]
LDR Rd, [Rn, #offset]
Базовая с непосредственным
Базовая адресация с
смещением указателя
непосредственным смещением
указателя относительно базы
Rd ← [(Rn) + #offset]
Использование в качестве базового регистра счетчика команд PC или указателя стека SP
LDR Rd, [PC, #offset]
Относительная – по содержимому
Базовая с непосредственным
счетчика команд PC и
смещением относительно
непосредственному смещению
содержимого счетчика команд
PC
Rd ← [(PC) + #offset]
LDR Rd, [SP, #offset]
Стековая – по текущему
Базовая с непосредственным
содержимому указателю стека SP и
смещением относительно
непосредственному смещению
указателя стека SP
Rd ← [(SP) + #offset]
LDR Rd, [Rn, Rm]
Базово-индексная (Rn – базовый, Rm – Базовая со смещением,
индексный регистры)
заданным содержимым
индексного регистра
Rd ← [(Rn) + (Rm)]
Псевдокоманда: считать Относительная – по содержимому
Косвенная с предварительным
код по метке
счетчика команд PC и
смещением относительно
LDR Rd, Label
непосредственному смещению
текущего содержимого
Ассемблер автоматически
счетчика команд PC
рассчитывает смещение до метки
Rd ← [(PC) + #offset]
относительно PC и генерирует
команду:
LDR Rd, [PC, #offset]
Псевдокоманда:
Загрузка данных с возможным
Загрузка непосредственных
загрузить
32-разрядное созданием литерального пула в
данных или данных из
слово
кодовой памяти
предварительно автоматически
LDR Rd, =Value
В зависимости от величины
созданного на этапе трансляции
значения Value генерируется либо
литерального пула в кодовой
команда непосредственной загрузки
памяти (из области констант)
MOV Rd, #Value,
либо команда косвенной загрузки с
предсмещением относительно PC
LDR Rd, [PC, #offset]
MOV Rd, Op2
Op2 – Rn;
Op2 - #Const
Op2 – Rn, Shift

Второй универсальный операнд:
Регистровая
Непосредственная
Регистровая с предварительным
сдвигом операнда на заданное число
разрядов

177

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ

10.2 Команды пересылки данных
Для инициализации регистров ЦПУ начальными значениями часто
применяется команда MOV с непосредственным операндом #imm. Она
является частным случаем загрузки регистра содержимым универсального
второго операнда Op2, который представляется в трех вариантах:
MOV{S}{cond} Rd, Op2
MVN{S}{cond} Rd, Op2
Второй операнд-источник: 1) #imm; 2) Rm; 3) Rm, Shift
Регистр назначения (приемник данных)
Код условного выполнения команды (опция)
Суффикс «S» (Set) установки флагов результатов операции (только флагов N и Z)
N (NOT) – НЕ, предварительная побитовая инверсия значения в операнде-источнике
MOV – (MOVE) – Перемещение (Загрузка) 32-разрядных слов из операнда источника Op2

Здесь и далее при изучении системы команд мы будем выделять красным цветом
корень в мнемокоде команды, синим цветом – операнд/ы-приемники, зеленым цветом –
операнды-источники данных. Дополнительными цветами (черным, фиолетовым и др.)
будем отмечать префиксы и суффиксы, которые модифицируют основную операцию.
Если соответствующая опция является необязательной, то она помещается в фигурные
скобки “{}”. Так, в нашем случае опция «S» позволяет дополнительно установить флаги N
и Z в регистре статуса программы пользователя, а опция {cond} может быть одним из
стандартных условий выполнения данной команды. Напомним, что если указанное Вами
условие ложно, то операция не выполняется, и процессор автоматически переходит к
выполнению следующей команды. Обратите внимание на то, что в отличие от многих
других процессоров, в которых операции пересылки данных не могут формировать флаги,
в процессорах ARM это возможно (могут модифицироваться два флага – отрицательного
результата N и нуля – Z).
Второй операнд Op2 в этой команде универсальный: непосредственный операнд
#imm, заданный любым арифметическим выражением; регистр-источник данных или
регистр-источник, содержимое которого перед загрузкой подвергается дополнительной
обработке в кольцевом сдвиговом регистре ЦПУ. Суффикс «N» позволяет выполнить
загрузку данных с еще одной попутной операцией – побитовом инвертированием
значения второго операнда. По сути – это будет уже логическая операция побитового
«НЕ» с загрузкой результата в регистр назначения.
Мы уже упоминали о том, что не все 32-разрядные константы могут быть заданы в
качестве непосредственных операндов, а только такие, значения которых могут быть
«сжаты» для размещения в формате 32-разрядной команды (см. 9.9).
Интересно, что транслятор с Ассемблера автоматически распознает среди всех
констант 16-разрядные константы и генерирует специальную версию команды пересылки,
которая обеспечивает загрузку данных только в младшее полуслово регистра назначения c
автоматической очисткой старшего полуслова:
MOVW{cond} Rd, #imm16
Дополнительный суффикс «W» (может быть опущен), указывает на то, что
загружается 16-разрядная константа (полуслово). Вместе с представленной ниже
командой загрузки 16-разрядной константы в старшее полуслово регистра назначения
программист получает возможность последовательной инициализации любого регистра
ЦПУ любой 32-разрядной константой независимо от ее значения. Единственное, что
нужно сделать – это разбить константу на две составляющие, младшее и старшее
178

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
полуслово, и последовательно загрузить в регистр назначения сначала младшее полуслово
(командой MOVW), а затем старшее (командой MOVT). Синтаксис команд загрузки 16разрядных констант:
MOVW{cond} Rd, #imm16
MOVT{cond} Rd, #imm16
16-разрядный операнд-источник данных (полуслово)
Регистр назначения (приемник данных)
Код условного выполнения команды (опция)
W - Младшего полуслова с автоматической очисткой старшего
T - Старшего полуслова с сохранением значения младшего полуслова. Флаги
модифицируются
MOV – (MOVE) – Перемещение (Загрузка) 16-разрядного полуслова в регистр назначения

не

Проверим, как работают команды
загрузки регистров ЦПУ (проект CPU_1 с
ранее описанным стартовым файлом
StartUP_1.s).
Программа
пользователя
MyProg_1.s сначала выполняет загрузку
регистров различными непосредственными
константами, затем загрузку из других
регистров процессора, уже содержащих
данные и, наконец, загрузку из регистров
процессора с предварительной обработкой
операндов в кольцевом сдвиговом регистре.
Первая команда из этой группы выполняет
предварительный логический сдвиг влево
на 8 разрядов, вторая – арифметический
сдвиг вправо на столько же разрядов, а
последняя – циклический сдвиг на 1 разряд
вправо.
Дальше
демонстрируется
выполнение
операции
логического
побитового «НЕ» второго операнда с
загрузкой результата по месту назначения.
В
конце
программы
выполняется
последовательная загрузка 32-разрядных
констант с помощью двух команд загрузки сначала младшего, а затем старшего полуслов.
В программе демонстрируются также способы ввода непосредственных констант в разных
системах счисления.
Выполним трансляцию файлов проекта и запустим программу на выполнение в
пошаговом режиме. Проследим за тем, как меняются значения в регистрах назначения.
Убедимся, что загрузка регистров (r0-r12) 32-разрядными операндами
выполняется правильно. Для этого обратимся к «Регистровому окну», в
котором по умолчанию все значения отображаются в шестнадцатеричной
системе счисления, или проверим текущее содержимое каждого регистра
непосредственно в окне исходной программы, наводя курсор на
символическое имя нужного регистра – его текущее содержимое будет тут
же выдано в окно исходной программы.
Продолжим пошаговое выполнение программы и проследим за тем, как
выполняется последовательная загрузка регистров (r2, r3) двумя полусловами. Вначале
загружаются младшие полуслова, а старшие автоматически обнуляются:

.

179

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Затем выполняется «дозагрузка» старших полуслов при сохранении значений младших:
Как видите, последовательная загрузка 32-разрядного слова работает.

3)
4)

5)
6)

2)

3)

4)

1) Докажите, что команда в строке 21 программы выполняется
правильно?
2) Если в команде загрузки есть две попутные операции – сдвига и
побитового инвертирования, то какое действие выполняется первым?
Докажите на примере команды в строка 25 программы.
Какая технология кодирования 32-разрядных операндов использована в командах в
строках 10-14? Проверьте Ваши предположения, изучив файл листинга.
Теперь попробуйте задать произвольную 32-разрядную константу, которая не
может быть закодирована методами выше. Оттранслируйте программу. Что
означает полученное сообщение об ошибке в окне вывода?
Как поступить в этом случае? Измените программу и выполните ее отладку.
Какую команду нужно использовать, чтобы побитово проинвертировать
содержимое регистра r1, т.е. фактически выполнить команду «Логическое НЕ»?
Проверьте в отладчике.
1) В регистр r2 было загружено число 0xF3F3F3F3, которое
процессор рассматривает как 32-разрядное отрицательное (старший бит
равен 1). Следовательно, при арифметическом сдвиге вправо на 8 разрядов
младший байт 0xF3 будет полностью вытеснен за пределы разрядной
сетки и потерян, а в старшие 8 разрядов будет «расширен» знаковый
разряд исходного числа, т.е. «1». Получим 0xFFF3F3F3.
Вначале выполняется операция извлечения исходных данных из регистраисточника (0xFA) и обработка в регистре сдвига с получением числа 0xFA00.
После этого результат побитово инвертируется и сохраняется по месту назначения
(0xFFFF05FF).
Фрагмент файла листинга свидетельствует о том, что константы могут быть заданы
двумя
шестнадцатеричными
цифрами «XY» и величиной сдвига
этого кода влево, а также
комбинациями двух HEX-цифр:
0xXYXYXYXY, 0x00XY00XY или
0xXY00XY00. Шифрация констант выполняется транслятором автоматически.
Изменим строку 10 программы:
. При трансляции
исходного файла получим в окне вывода сообщение, которое говорит о том, что

введенная нами константа 0xAB3477CD не может быть закодирована с
использованием возможных для этой команды методов кодирования: байта 0-255 и
дополнительного сдвига влево на (0-23) разряда; байта, который «размножается»
на все, четные или нечетные байты.
5) Можно разбить константу на два полуслова и загрузить полуслова по очереди.
Дальше мы покажем, как эту операцию можно
выполнить другим способом.

180

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ


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

10.3 Загрузка 32-разрядных констант. Относительная
адресация по содержимому счетчика команд.
Литеральные пулы.
Последовательная загрузка 32-разрядных констант младшим и
старшим полусловами не удобна для программистов. Однако, в 32разрядном процессоре нужна именно такая инициализация. При попытке
создать команду непосредственной адресации 32-разрядных операндов мы
сталкиваемся с принципиальным ограничением архитектуры RISCпроцессоров Reduced Instruction Set Computer (компьютеров с
сокращенным набором команд), в строгом соответствии с которой должно работать
процессорное ядро Cortex-M3/M4. По этой идеологии, все команды должны иметь
одинаковый формат (32 разряда) и выполняться одинаково быстро – за один такт. Однако,
при использовании 32-разрядного непосредственного операнда в формате 32-разрядной
команды не остается ни одного свободного разряда для кодирования операции. Выход из
этого «тупика» разработчики ARM нашли оригинальный: предложили использовать
загрузку таких «длинных» констант с использованием относительной адресации по
текущему состоянию счетчика команд PC и дополнительному непосредственному
смещению до места расположения константы в кодовой памяти. Проблема выполнения
такой команды за один такт была решена аппаратно за счет введения в архитектуру
процессора сразу двух шин, подключенных к кодовой памяти - ICode (шины считывания
инструкций) и DCode (шины считывания данных), которые при выполнении команд
загрузки «длинных» констант работают параллельно. В результате чего константа может
«считываться» из памяти программ на фоне выполнения кода команды загрузки, не
замедляя работу конвейера команд (см. 5.2.4). Потребовалось усовершенствовать и
транслятор с Ассемблера, чтобы он мог вместе с командами «длинной» загрузки
автоматически генерировать и сами константы в кодовой памяти. Для реализации этой
технологии в язык Ассемблер ARMASM включена специальная псевдокоманда
«длинной» загрузки регистров ЦПУ:
LDR Rd, =Const
Попробуем использовать ее на
практике (проект CPU_2): В состав
проекта входит тот же стартовый
файл StartUp_1.s и файл пользователя
MyProg_2.s. В нем выполняется инициализация
первых
четырех
регистров
процессора
начальными значениями. Выполните трансляцию
исходного файла и изучите файл листинга
(показан его фрагмент): все псевдокоманды
преобразованы в машинные коды и могут быть
выполнены.
181

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Проверим
это.
Создадим
выходной объектный файл проекта и
запустим
процесс
отладки
в
симуляторе. Выполним программу в
пошаговом режиме и проанализируем результат в окне регистров:
загрузка выполнена абсолютно правильно. Но почему же тогда в файле
листинга команды имеют не только разную кодировку, но и разный
формат (16 и 32 разряда)?
Ответ на этот вопрос становится
понятным, если открыть окно дизассемблера
и
просмотреть
реально
создаваемый
транслятором код. Красным цветом в этом
окне выделен исходный код программы,
написанный
с
использованием
псевдокоманд, а черным – реально
сгенерированный код, который будет на
самом деле выполнять процессор. Так,
вместо псевдокоманды LDR r0, =0xAB3477CD процессор будет выполнять команду, LDR
r0, [pc, #12], входящую в его систему команд. Это команда косвенной загрузки регистра r0
из ячейки памяти, адрес которой определяется как текущее содержимое счетчика команд
PC плюс указанное после запятой смещение: (PC)+#12 (указывается в десятичной системе
счисления). После точки с запятой дизассемблер подсказывает, по какому реальному
адресу последует обращение за данными: @0x00000028 (этот адрес, как и адрес
размещения кодов команд в самом левом столбце окна, указывается в шестнадцатеричной
системе счисления).
Такой способ адресации данных называется относительной адресацией по
содержимому счетчика команд PC. Возникает естественный вопрос: а что считается
текущим содержимым счетчика команд PC? Это - начальный адрес возможного
размещения в памяти следующей команды, но обязательно кратный 4 (двукратно
четный). Проверим это на примере первой команды LDR r0, [pc, #12]: она является
16-разрядной и расположена в памяти по адресу 0x18. Следующая команда будет
находиться по адресу 0x18+2= 0x1A, который не выровнен по границе полного слова (не
кратен 4). В качестве текущего значения PC принимается очередной дважды четный адрес
0x18+4=0x1C. Добавим указанное в команде смещение 12d=0x0C, получим адрес 0x28,
который Дизассемблер и указал в комментарии к этой команде. Что за данные
располагаются по этому адресу? Мы видим, что с помощью директив размещения в
кодовой памяти константы-полуслова DCW транслятор разместил здесь сначала код
0x77CD, а затем код 0xAB34, то есть заданную нами 32-разрядную константу
0xAB3477CD.
При использовании псевдокоманды LDR Rd,=literal загрузки данных
в регистр ЦПУ транслятор автоматически создает в кодовой памяти так
называемый литеральный пул, то есть область памяти, содержащую
значение требуемой константы. В процессе выполнения команды
относительной загрузки данных по содержимому PC выполняются два
действия: 1) по кодовой шине ICode, подключенной к памяти программ, считывается код
операции, определяется смещение и рассчитывается эффективный адрес доступа к
данным в программной памяти (PC+offset); 2) эффективный адрес доступа выставляется
на шину данных DCode и 32-разрядный код данных считывается в процессор и
загружается в регистр-приемник ЦПУ.
Обе эти операции выполняются параллельно, следовательно, вся команда загрузки
регистра выполняется за один такт, как и положено в процессорах с RISC-архитектурой.
182

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Цена за решение проблемы загрузки 32-разрядных кодов – это вставки в программный код
– литеральные пулы, являющиеся по существу константами, автоматически
размещенными в кодовой памяти транслятором и используемыми в командах загрузки
слов с относительной адресацией по текущему содержимому PC.
В псевдокоманде загрузки регистра ЦПУ 32-разрядным словом LDR Rd,=literal
после знака равенства символ непосредственного операнда «#» не ставится. Константа
может быть задана в любой системе счисления или арифметическим выражением,
компоненты которого определены заранее.
Продолжим исследование нашей программы. Обратим внимание на то,
что две последующие команды загрузки превратились в обычные команды
MVN и MOV, рассмотренные нами ранее. Таким образом, транслятор с
Ассемблераявляется интеллектуальным: он автоматически генерирует код,
который может быть выполнен быстрее и не требует размещения в кодовой памяти
дополнительных констант в виде литеральных пулов, экономя также память системы.
Последняя псевдокоманда заменяется командой относительной загрузки по содержимому
PC с генерацией еще одной константы. Она размещается вслед за уже созданной
константой, то есть литеральный пул по мере трансляции программы автоматически
расширяется (см. содержимое окна дизассемблера).
Заметьте, что между последней командой программы и литеральным пулом
транслятор вставил нулевое полуслово-шаблон (директива размещения полуслова в
кодовой памяти DCW 0x0000). Это происходит автоматически для того, чтобы все данные
в литеральном пуле были выровнены по границе полного 32-разрядного слова. Только в
этом случае доступ к константам в кодовой памяти по шине DCode будет оптимизирован,
и любая команда загрузки регистра будет выполняться за один такт. О том, что транслятор
выполнил вставку такого шаблона, Вы получите предупреждение в окне вывода при
трансляции исходного файла: «Добавлено 2 байта шаблона по адресу 0xe».
Предупреждение не
является ошибкой, и
с проектом можно
работать
дальше.
Впрочем, если хотите избавиться от предупреждения, используйте в конце своей
программы директиву выравнивания последующего кода по границе полного слова
ALIGN (это и будет адрес размещения литерального пула).
1) Действительно ли команда MVN r1, #02 эквивалентна команде
LDR r1,=-3?
2) В чем преимущество генерации команды MOV r2, #0xF0 по
сравнению с генерацией команды LDR r2,= literal? Ведь она является 32разрядной командой из набора ARM, то есть более длинной?
3) Правильно ли транслятор сгенерировал смещение в последней команде?
4) Уберите признак комментария перед директивой ALIGN и выполните повторную
трансляцию программы. Что изменилось в файлах листинга?
5) Попробуйте задать константу арифметическим выражением, компоненты которого
определены. Программа работает?

183

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
1) Да, команда MVN побитово инвертирует значение в операндеисточнике 0x2 и загружает результат в регистр-приемник. Получаем число
0xFFFFFFFD, что в точности соответствует числу (-3) в дополнительном
коде.
2) Команда относительной загрузки по содержимому счетчика
команд действительно короче – 2 байта. Но не забывайте о том, что в кодовой
памяти потребуется разместить литеральный пул с константой – 32-разрядное
слово. Общие затраты кодовой памяти будут на 2 байта больше. Именно поэтому
транслятор автоматически выбирает более эффективный вариант.
3) Да. Адрес размещения текущей команды 0x22, а следующей – 0x24 (так как
текущая – 16-разрядная). Он удовлетворяет условию «дважды четный» и
принимается за текущее содержимое PC. Добавляя смещение #8, получим
0x24+0x08=0x2C. Именно по этому адресу и размещена в кодовой памяти
константа 0xFA5577BE.
4) В первом случае транслятор выполняет
работу за программиста автоматически,
размещая нулевое полуслово-шаблон после
последнего кода программы для того, чтобы
литеральный пул был выровнен по дважды
четному адресу. Во втором случае эту же
операцию выполняет сам программист, зная,
что он использовал псевдокоманды загрузки регистров ЦПУ, которые будут
сопровождаться авто-генерацией литеральных пулов. В последнем случае
транслятор не выдает предупреждений.
5) Да. Например, введите в качестве константы выражение: 2_1100+37. Сумма
двоичного и десятичного чисел будет равна 12+37=49=0x31. Транслятор вычислит
выражение сам и сгенерирует
правильную команду

10.4 Директива принудительного размещения литеральных
пулов
По
умолчанию
транслятор
размещает
«литеральные пулы» в конце
текущей кодовой секции.
Предполагается, что зона констант
находится недалеко от места размещения
собственно программного кода, поэтому
обратиться
к
ней
можно
с
использованием
относительной
адресации – по базовому адресу в
счетчике
команд
PC
и
непосредственному смещению #offset,
величина которого находится в диапазоне
(-255÷4095).
Такое
12-разрядное
смещение можно разместить в 32разрядном поле команды Thumb-2.
184

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
В ряде случаев диапазона возможных смещений может не хватить. Например,
если программист зарезервировал в конце текущей кодовой секции большой объем
памяти для последующих расширений (проект CPU_3). Выйти из положения можно,
принудительно указав место расположения литеральных пулов с помощью директивы
Ассемблера LTORG.
Она не требует никаких дополнительных операндов и своим расположением лишь
указывает место, куда транслятор может разместить литеральный пул. Это может быть
промежуток между отдельными подпрограммами или между основной программой и
подпрограммами, как показано в этом примере. Из фрагмента листинга видно, что
константа действительно размещается там, где указал программист (см. строку 16).
Напомним,
что
директива Ассемблера SPACE
резервирует
определенный
объем памяти (в том числе
кодовой).
Если
она
применяется в кодовой секции,
то зарезервированная область
памяти обнуляется (см. строку
24). Мы выделили 5000
резервных байт, поэтому размещение литерального пула в конце текущей кодовой секции
стало невозможным. Пришлось размещать его между основной программой и
подпрограммой.

10.5 Загрузка данных из кодовой памяти по адресам
размещения данных
Ассемблер позволяет не только резервировать определенные области в
кодовой памяти, как это было показано выше, но и инициализировать эти
области, размещая в них нужные программисту константы. Константы могут
помечаться метками, значения которых будут соответствовать текущим
адресам расположения констант. Напомним, что для резервирования констант в памяти
используются следующие директивы Ассемблера:
{Label} DCD{U} expr{,expr}…
{Label} DCW{U} expr{,expr}…
{Label} DCB
expr{,expr}…
Выражения, задающие значения констант в памяти
U (Unaligned) – Без выравнивания по границе слова (4-м байтам) или
полуслова (по 2-м байтам) – размещение данных подряд
D – резервировать полное 32-разрядное слово или несколько слов
W – резервировать полуслово (16 разрядов) или несколько полуслов
B – резервировать байт (8 разрядов) или несколько байт
DC – (Define Code) Определить код в памяти (константу)
Label – Необязательная метка

Без суффикса «U» размещение слов в памяти выполняется с автовыравниванием по
границе слова (4-м байтам), полуслов – с автовыравниванием по границе полуслова (двум
байтам). Байты размещаются подряд, без выравнивания. Если опция «U» в первых двух
директивах присутствует, то слова и полуслова размещаются в памяти без выравнивания.

185

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
При автовыравнивании транслятор автоматически вставляет перед резервируемой
константой нужное число «нулевых» байтов - шаблонов.
Выражения для задания полуслов должны быть в диапазоне целых 16-разрядных
чисел без знака или со знаком, то есть от -32768 до 65535. Выражения для задания байт
должны быть в диапазоне целых байт со знаком или без знака, то есть от -128 до 255.
Байты можно задать и последовательностью символов, каждый из которых заключается в
одиночные кавычки, или строкой символов, заключенной в двойные кавычки.
Пример использования директив резервирования констант в памяти
можно найти в проекте CPU_4. Программа приложения содержит четыре
псевдокоманды загрузки регистров ЦПУ по фактическим адресам размещения
данных в памяти: LDR Rd, Label, а в конце программы расположены
директивы резервирования и инициализации
памяти двумя 32-разрядными словами, двумя
полусловами и четырьмя байтами. Обратите
внимание на то, что в каждой из директив, через
запятую, можно задать любое требуемое число
констант. Всего в памяти проинициализировано
четыре слова, каждое из которых получило свое
символическое имя Const_0, Const_1,… Именно
эти имена использованы в псевдокомандах
загрузки данных по их меткам.
Просмотрите
листинг
этой
программы. Обратите внимание на то,
что транслятор перед директивой DCD
резервирования
слова
выполнил
автовыравнивание памяти по границе
слова (добавил два «нулевых» байта),
выдав
соответствующее
предупреждение
в
окно
вывода
результатов.
Обратите

также

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

Как же работают псевдокоманды загрузки данных по адресам? Обратимся к окну
дизассемблера и посмотрим, какие команды на самом деле выполняет процессор вместо
псевдокоманд. Вы видите знакомые команды относительной адресации памяти по
содержимому счетчика команд PC и непосредственно заданному в команде смещению.
Давайте проследим, к каким областям памяти обращается при этом процессор. В первом
случае – к данным по адресу 0x24, где
находится первая записанная в память
константа, во втором случае – по адресу
0x28,
месту
расположения
второй
константы и т.д.

186

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Итак, для доступа к данным по их символическим адресам можно
использовать директивы загрузки регистров по меткам, которые присвоены
константам при их размещении в памяти. Единственное отличие этих
псевдокоманд от псевдокоманд с автоматическим созданием в памяти
литеральных пулов состоит в том, что данные в памяти резервирует сам
программист. Иногда это бывает полезно, так как делает программу более понятной.

10.6 Косвенная адресация операндов в памяти
10.6.1 Общие положения
Один из основных способов адресации операндов в памяти,
применяемый в процессорной технике – косвенная адресация. Ее
признаком является использование для обозначения операнда квадратных
скобок […], внутри которых указывается символическое имя регистрауказателя – регистра, содержащего адрес ячейки памяти, к которой
производится доступ. В общем случае после имени регистра-указателя,
через запятую, внутри квадратных скобок можно задать смещение относительно текущего
содержимого
регистра-указателя
[Rn,#offset].
Такое
смещение
называется
непосредственным. Его значение добавляется к содержимому базового регистра перед
доступом к данным. При этом исходное содержимое базового регистра не
модифицируется. Задавая разные величины смещений можно получить доступ к любым
элементам массива, начальный адрес которого находится в регистре-указателе. В системе
команд ARM-процессоров регистр-указатель называют базовым регистром. Если
смещение указано, то такая адресация называется базовой с непосредственным
смещением.
Если величина смещения указывается через запятую после квадратных скобок
[Rn],#offset, то такая адресация называется косвенной адресацией с пост-смещением
указателя: сначала производится доступ к данным по текущему содержимому указателя, а
затем к нему добавляется смещение.
Как видите, способ адресации в псевдокоманде загрузки LDR Rd,=Value
(относительная адресация по текущему содержимому счетчика команд) по существу
является косвенной базовой адресацией с непосредственным смещением, в которой
функцию базового регистра выполняет счетчик команд [PC,#offset]. Единственное
отличие состоит в том, что после доступа к данным по эффективному адресу,
рассчитанному как (PC)+#offset, счетчик команд PC автоматически перезагружается
адресом следующей, подлежащей выполнению команды (получает приращение +2 или +4
в зависимости от длины текущей команды).
Итак, относительная адресация по содержимому счетчика команд – это частный
случай косвенной базовой адресации с непосредственным смещением, заданным в
формате самой команды. Какой может быть величина смещения? Для набора команд
Thumb-2 смещение кодируется 12-разрядным числом и имеет диапазон -255÷4095. Это
означает, что обращение за данными, расположенными в литеральном пуле выполняется в
основном вперед.

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

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
специальной псевдокомандой ассемблера «Загрузить в регистр назначения адрес метки».
Она работает в том случае, когда данные расположены в той же секции на ограниченном
удалении от команды и имеет следующий синтаксис:
ADR{cond}{.W} Rd,label
Компонент
псевдокоманды

Назначение

cond
.W

опциональный суффикс условного выполнения команды
опциональный спецификатор ширины генерируемой команды. Если указан, то
длина команды составляет 32 разряда, в противном случае – 16 разрядов.
имя регистра-приемника данных (регистра-указателя)
метка, символическое имя данных в памяти

Rd
label

Псевдокоманда генерирует реальную команду загрузки в регистр назначения
адреса, который рассчитывается как текущий адрес в счетчике команд плюс заданное в
команде непосредственное смещение ADR Rd, (PC)+#offset. Величина нужного смещения
рассчитывается транслятором автоматически. В режиме 32-разрядного кодирования
значение смещения не должно превышать ±4095, а в режиме 16-разрядного кодирования
должно находиться в диапазоне 0÷1020. В этом случае ссылка на данные возможна только
вперед, в направлении возрастающих адресов. Если генерация смещения в допустимом
диапазоне невозможна, то транслятор выдает сообщение об ошибке. Первое, что нужно
сделать в этом случае – использовать опцию «.W» с более широким диапазоном
смещений. Если это также не даст результата – воспользоваться уже рассмотренной нами
псевдокомандой загрузки LDR Rd,=Value, которая за счет создания литерального пула
позволяет обращаться к данным по любым адресам в памяти объемом 4 Гбайта.
Создаваемый командой ADR код является позиционно независимым. Это
позволяет размещать вместе с кодом программы данные и таблицы данных, к которым
обеспечивается простой доступ из той же текущей кодовой секции командами c
косвенной адресацией.
1) Команду ADR можно использовать и для последующей косвенной
передачи управления командами BX или BLX. При этом Вы должны сами
установить признак набора команд Thumb, в котором процессор должен
продолжать работать после завершения перехода (установить бит 0 адреса
в «1»). В процессе выполнения команды косвенной передачи управления
он будет автоматически очищен и переход состоится в нужную точку программы.
2) Не рекомендуется использовать в команде ADR счетчик команд PC и указатель
стека SP в качестве регистра приемника.

10.6.3 Доступ к данным с использованием косвенной адресации
Откроем проект CPU_5 с
демонстрацией основ косвенной
адресации операндов. В программе
приложения MyProg_5.s вначале
выполняется инициализация регистра-указателя
r0 адресом расположения в памяти константы
Const_0 с использованием псевдокоманды ADR.
Далее выполняется команда косвенной загрузки
регистра r5 данными по текущему адресу в
регистре-указателе r0. После чего указатель r0
трижды переинициализируется для доступа к
188

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
остальным константам, и эти константы загружаются в регистры r6, r7, r8. Как и в
предыдущем проекте, в конце программы в текущей кодовой секции размещаются четыре
константы-слова.
Рассматривая содержимое файла листинга,
убеждаемся в том, что транслятор смог
сгенерировать короткие 16-разрядные команды
ADR, так как данные расположены на небольшом
удалении от команд. В листинге указаны
псевдокоманды.
Обратитесь
к
окну
дизассемблера, чтобы посмотреть, что они
представляют собой в реальности: псевдокоманды
заменяются командами ADR Rd, {PC}+#offset.

Реальные адреса обращения к
данным указаны в комментариях к
созданным командам. Они точно
соответствуют месту расположения
констант в памяти. Так, константа Const_1 действительно расположена по адресу 0x30.
Обозначение {PC} говорит о том, что это не истинный адрес расположения текущей
команды в памяти, а дополнительно модернизированный кодом операции. В нашем
случае увеличенный на 0x10 (0x1C+0x10+4=0x30). Будем доверять транслятору в
формировании кодов операций, не вдаваясь в детали кодирования команд.
1) Постройте проект, загрузите в отладчик и выполните в пошаговом
режиме. Проследите за тем, как последовательно меняется содержимое
регистра-указателя r0 и как работает команда косвенной загрузки данных
по его текущему содержимому LDR Rd, [r0].
2) Вам не кажется, что каждый раз выполнять новую перезагрузку
регистра-указателя, если данные расположены в памяти последовательно, не имеет
смысла? Проще добавить к значению в регистре-указателе нужное смещение и
считать очередные данные. Попробуйте сделать это с использованием команды
инкрементирования содержимого регистра указателя r0 на 4 (ADD r0, #4).
Проверьте работу программы.
3) До сих пор мы пользовались обычными командами косвенной загрузки. Но, если
данные расположены последовательно, целесообразно применить команду с поставто-инкрементированием указателя. В этом случае вслед за именем регистрауказателя в квадратных скобках через запятую нужно задать величину смещения.
Сделайте это. Выполните отладку.
4) Перед Вами задача. Извлечь из массива слов в кодовой памяти вторую и четвертую
константы и найти их сумму с записью результата в регистр r5. Попробуйте
сделать это.

189

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
1) Загрузка данных выполнена правильно. Регистр
r0 cодержит адрес последней константы Const_3
– 0x38, которая после выполнения программы
скопирована в регистр R8. Обратите внимание на
то, что все команды косвенной адресации в
действительности генерируются с нулевым непосредственным
смещением [r0, #0x00]. В ассемблерной программе смещение может быть опущено
(при этом оно по умолчанию считается нулевым).
2) Фрагмент модифицированной программы
MyProg_5_1.s представлен ниже. Директивы
резервирования констант остались без
изменения в конце программы (не показаны).
Перед
каждой
загрузкой
содержимое
регистра указателя увеличивается на +4.
Заметьте, что команда сложения – двухоперандная:
регистр
r0
является
одновременно как источником, так и
приемником данных.
3) Фрагмент модифицированной программы
My_Prog_5_2 показан ниже. Косвенная
адресация с пост-авто-смещением указателя
позволяет сначала выполнить доступ к
данным, а затем, той же командой
модифицировать содержимое указателя (+4).
При исследовании файла листинга обратите
внимание на то, что эти команды уже 32разрядные.
4) Для решения задачи необходима
косвенная
адресация
памяти
с
непосредственным смещением [r0,
#offset].
При
этом
содержимое
указателя r0 не модифицируется, а
доступ к данным выполняется по
эффективному адресу, равному сумме
содержимого регистра-указателя и
заданного в команде непосредственного смещения. Если оставить в r0 начальный
адрес нашего массива, то для доступа ко второй константе смещение должно быть
равно +4, а к четвертой +12. Возможный фрагмент такой программы
(MyProg_5_3.s) показан ниже: Результат ее работы доказывает, что и загрузка
элементов массива, и их обработка, выполнены правильно.
Используйте этот метод для выборки нужных элементов
массива, задавая нужные величины смещений.

190

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Мы познакомились с рядом псевдокоманд Ассемблера, которые
позволяют загружать регистры ЦПУ данными из кодовой памяти по их
символическим адресам LDR Rd, label, загружать данные с одновременным
размещением их в кодовой памяти (в литеральных пулах) LDR Rd,=literal,
загружать в регистры процессора адреса расположения данных ADR Rd,
label для последующего использования этих регистров в качестве регистров-указателей в
командах косвенной адресации памяти. Во всех этих псевдокомандах фактически
используются специальные команды процессора с относительной адресацией по
содержимому счетчика команд PC и автоматически рассчитанному транслятором
непосредственному смещению.
В ряде случаев интеллектуальный транслятор может сгенерировать более
простую команду, в частности, команду загрузки в регистр непосредственной константы.
При использовании регистров младшего регистрового банка (R0-R7) вероятность
получения более компактного 16-разрядного кода существенно возрастает. Пользуйтесь,
по возможности, этими регистрами.

10.7 Команды работы с памятью
Архитектура
процессоров
ARM
является
архитектурой
Загрузки/Сохранения в том смысле, что все вычислительные операции
выполняются исключительно над данными, находящимися в регистрах
процессора – сверхоперативной памяти процессора. Поэтому, прежде чем
обрабатывать данные, их необходимо загрузить в регистры из памяти или
регистров периферийных устройств, а после обработки – сохранить в
памяти или в регистрах периферии (выдать результат обработки).

10.7.1 Какие данные используются в операциях загрузки/сохранения?
Память в ARM-процессорах байтовая, каждый байт имеет свой персональный
адрес, как и каждое 16-разрядное полуслово (четный адрес) и каждое 32-разрядное слово
(дважды четный адрес, кратный 4). Все регистры процессора 32-разрядные. Данные в них
можно интерпретировать как 32-разрядное слово, два 16-разрядных полуслова (младшее и
старшее) или четыре 8-разрядных байта – младший и старший в каждом из двух полуслов.
Как процессор выполняет загрузку данных из памяти?
• 32-разрядное слово загружается в регистр процессора целиком без каких-либо
изменений;
• 16-разрядное полуслово загружается в младшее полуслово регистра «как есть».
Далее все зависит от типа полуслова, специфицированного в команде загрузки
дополнительным суффиксом:
o H (полуслово без знака) – во все старшие биты регистра записываются нули;
o SH (полуслово со знаком) – во все старшие биты записывается знаковый
разряд числа.
• 8-разрядный байт загружается в младший значащий байт регистра «как есть».
Далее все зависит от типа байта, заданного дополнительным суффиксом в команде
загрузки:
o B (байт без знака) – во все старшие биты регистра записываются нули;
o SB (байт со знаком) – во все старшие биты записывается знаковый разряд
числа.

191

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Таким образом, операция загрузки слова из памяти в регистр процессора
представляет собой обычную операцию пересылки данных, а операция загрузки
полуслова или байта – автоматического преобразования формата числа из 16разрядного или 8-разрядного в 32-разрядный формат. Для этого преобразования
процессору необходимо «знать», является ли исходное число числом без знака или числом
со знаком в дополнительном коде. Если исходное число является беззнаковым (в команде
указан суффикс H или B), то выполнятся операция беззнакового расширения, в противном
случае (указан суффикс SH или SB) – операция знакового расширения. Это одно из
преимуществ системы команд ARM-процессоров. Данные могут храниться в памяти в
укороченных форматах (полусловах, байтах), а их обработка будет выполняться в
расширенном 32-разрядном формате. При этом загрузка «укороченных» данных
сопровождается автопреобразованием формата, и никаких дополнительных операций для
этого не требуется.
Операции сохранения содержимого регистров в памяти выполняются без
дополнительных преобразований данных – данные сохраняются «как есть»: полное слово,
полуслово или байт, причем команды с суффиксами (B и SB), (H и SH) просто сохраняют
в памяти байт или полуслово соответственно. Впрочем, один из пары суффиксов, Вы
обязательно должны указать, так как при его отсутствии в памяти будет сохранено полное
4-байтовое слово. По умолчанию, если дополнительный суффикс не указан, то команда
работает с полным 32-разрядным словом.

10.7.2 Форматы команд загрузки/сохранения данных
Форматы команд загрузки регистров ЦПУ из памяти (LDR) и сохранения
значений регистров в памяти (STR) с возможными опциями показаны ниже:
Оp{type} {cond} Rt, [Rn {, #offset}]
Оp{type} {cond} Rt, [Rn, Rm {, LSL #n}]
Оp{type}T {cond} Rt, [Rn {, #offset}]
Способ адресации источника (LDR)/приемника (STR) данных в памяти
Rn – базовый регистр; Rm – индексный регистр; #offset – непосредственное
смещение; LSL #n – логический сдвиг влево на #n разрядов
Rt – Регистр-приемник (LDR) или источник (STR) данных
cond – Код условного выполнения команды
Т – Опция загрузки/сохранения в режиме непривилегированного доступа
type – тип операции:
В
с байтом без знака
BS
с байтом со знаком
H
с полусловом без знака
HS
с полусловом со знаком
Op - Операция
LDR – Загрузка регистра из памяти
STR - Сохранение содержимого регистра в памяти

В отличие от всех других команд процессора, в которых имя регистра-приемника
данных всегда указывается на первом месте, сразу после кода операции, в командах
загрузки/сохранения на первом месте всегда находится имя регистра ЦПУ, участвующего
в операции (независимо от того, является ли он приемником или источником данных). На
втором месте указывается используемый в команде способ адресации памяти. Ниже мы
рассмотрим возможные способы адресации подробно. Здесь лишь отметим, что синтаксис
команд в первой и третьей строке соответствует базовой адресации с непосредственным
смещением содержимого указателя, а во второй строке – базово-индексной адресации.
192

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Значение спецификатора типа операции «type» было описано выше: оно определяет
формат данных, с которыми имеет дело процессор, и, следовательно, необходимость
знакового или беззнакового расширения в область старших разрядов (только при загрузке
данных в регистры).
Суффикс «T» – особый. Он заставляет процессор работать при выполнении
команды в режиме непривилегированного доступа, то есть в режиме, когда доступ к
регистрам специального назначения процессора заблокирован, во избежание случайного
непреднамеренного вреда (например, случайной переинициализации регистра управления
процессора).

10.7.3 Базовая адресация с непосредственно заданным смещением указателя
Возможности базовой адресации с непосредственным смещением очень широки.
Они раскрыты в табл. 10.2 с указанием выполняемых действий и преимущественной
области применения этого метода адресации.
Таблица 10.2 Варианты базовой адресации с непосредственным смещением указателя
Мнемоника

Способ адресации

Основное применение

[Rn]

Косвенная по содержимому базового
регистра Rn
Rt ← [(Rn)]
Базовая с непосредственно заданным
смещением #offset
Rt ← [((Rn)+#offset)]
Базововая с пре-смещением указателя на
константу #offset
Rn ← (Rn)+#offset;
Rt ← [(Rn)].
Базовая с пост-смещением указателя на
константу #offset
Rt ← [(Rn)]
Rn ← (Rn)+#offset;

Единичный доступ к данным, адрес
которых известен и находится в базовом
регистре Rn.
Единичный доступ к элементу массива с
известным
смещением
адреса
относительно начала массива #offset.
Последовательная обработка элементов
массива (в цикле) с пре-смещением
указателя перед каждым доступом на
величину #offset.
Последовательная обработка элементов
массива (в цикле) с пост-смещением
указателя после каждого доступа на
величину #offset.

[Rn, #offset]

[Rn, #offset]!

[Rn], #offset

Красным цветом выделены признаки дополнительных возможностей этого типа
адресации, которые позволяют выполнить либо пре-смещение, либо пост-смещение
содержимого указателя, что позволяет автоматизировать процесс обработки данных в
циклах.
10.7.3.1 Косвенная адресация (базовая с нулевым смещением)
Графическая иллюстрация классической косвенной адресации памяти, когда
доступ к данным производится по текущему адресу, находящемуся в базовом регистре
(регистре-указателе), показана на рис. 10.1. В этом случае содержимое регистра-указателя
не модифицируется и для доступа к новым данным его нужно каждый раз переинициализировать (как мы и делали в примере MyProg_5.s). Содержимое базового
регистра всегда указывает на младший адрес размещения данных в памяти независимо от
формата передаваемых данных (байт, слово, полуслово).
После извлечения данных из памяти содержимое этой ячейки памяти остается
прежним. При записи – меняется, но только в тех байтах, которые соответствуют типу
пересылаемых данных (для байта – только в младшем байте, для полуслова – в младшем
полуслове, для слова – во всех 4-х байтах). Напомним, что доступ к памяти в процессорах
ARM – побайтовый.

193

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Косвенная адресация
LDR Rt,[Rn]

STR Rt,[Rn]
0xFFFF FFFF

0xFFFF FFFF

(Rn)

0x0000 0000

Rt

(Rn)

Rt

0x0000 0000

Рис. 10.1 Косвенная адресация памяти при загрузке и сохранении данных
10.7.3.2 Базовая адресация с непосредственным смещением
Для этого типа адресации программист после имени базового регистра, через
запятую, указывает непосредственное смещение, которое перед доступом к данным
прибавляется к содержимому базового регистра. После доступа к данным значение
указателя остается прежним. Именно смещение будет определять так называемый
эффективный адрес доступа к данным Adr (красная стрелка на рис. 10.2). В системе
команд ARM-процессоров поддерживается именно косвенная адресация с
непосредственным смещением. Если смещение опущено (как для косвенной адресации),
то транслятор автоматически генерирует нулевое смещение. Последовательность
операций, выполняемых при записи и чтении данных, показана на рис. 10.2 цифрами.
Базовая адресация с непосредственным смещением
LDR Rt,[Rn, #offset]
STR Rt,[Rn, #offset]
0xFFFF FFFF

Adr

0xFFFF FFFF

0xFFFF FFFF

2

Adr

Rt

1

1

Adr=(Rn)+#offset

Adr=(Rn)+#offset

(Rn)
0x0000 0000

0xFFFF FFFF

2

Rt

(Rn)

(Rn)

(Rn)

0x0000 0000

0x0000 0000

0x0000 0000

Рис. 10.2 Базовая адресация с непосредственным смещением

194

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
10.7.3.3 Базовая адресация с пре-смещением
Адресация с пре-смещением отличается тем, что перед каждым доступом к данным
содержимое базового регистра (указателя) предварительно модифицируется: к нему
добавляется заданное в команде смещение – рис. 10.3.
Базовая адресация с пре-смещением
LDR Rt,[Rn, #offset]!
STR Rt,[Rn, #offset]!
0xFFFF FFFF

0xFFFF FFFF

2

(Rn)

0xFFFF FFFF

(Rn)

Rt

2

(Rn)

1

1

Rn=(Rn)+#offset

Rn=(Rn)+#offset

(Rn)

(Rn)

0x0000 0000

0xFFFF FFFF

0x0000 0000

Rt

0x0000 0000

(Rn)

0x0000 0000

Рис. 10.3 Базовая адресация с пре-смещением
10.7.3.4 Базовая адресация с пост-смещением
Адресация с пост-смещением модифицирует значение
(указателя) после выполнения доступа к данным – рис. 10.4.

базового

регистра

Базовая адресация с пост-смещением
LDR Rt,[Rn], #offset
STR Rt,[Rn], #offset
0xFFFF FFFF

0xFFFF FFFF

2

0xFFFF FFFF

(Rn)

2

0x0000 0000

(Rn)

Rn=(Rn)+#offset

Rn=(Rn)+#offset

(Rn)

0xFFFF FFFF

1

(Rn)

Rt
0x0000 0000

1

Rt

0x0000 0000

0x0000 0000

Рис. 10.4 Базовая адресация с пост-смещением

195

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ

10.7.4 Базовая адресация со смещением, заданным содержимым регистра, и
базово-индексная адресация
Величина смещения #offset может содержаться в одном из регистров общего
назначения процессора, который в этом случае называется регистром смещения Rm. Это
удобно, когда смещение объекта данных относительно базового адреса в регистре Rn
заранее не известно и должно быть вычислено. Мнемоника этого способа адресации
представлена в табл. 10.3.
Таблица 10.3 Мнемоника базовой адресации со смещением
Мнемоника

Способ адресации

Основное применение

[Rn, Rm]

Базовая со смещением в регистре
смещения Rm
Rt ← [(Rn)+(Rm)]

Доступ
к
данным
c
предварительным
вычислением
смещения и записью его в регистр
смещения Rm.

На рис. 10.5 дана графическая иллюстрация этого метода адресации.
Базовая адресация со смещением в регистре
LDR Rt,[Rn,Rm]
STR Rt,[Rn,Rm]
0xFFFF FFFF

Adr

0xFFFF FFFF

0xFFFF FFFF

2

Adr

Rt

1

1

Adr=(Rn)+(Rm)

Adr=(Rn)+(Rm)

(Rn)
0x0000 0000

(Rn)

0x0000 0000

0xFFFF FFFF

2

Rt

(Rn)
0x0000 0000

(Rn)

0x0000 0000

Рис. 10.5 Базовая адресация со смещением, заданным содержимым регистра
Данные в памяти часто представляют собой однородный массив (байт, полуслов,
слов, длинных слов), отдельный элемент которого a[i] определяется именем массива «a» и
индексом «i». Самый удобный метод адресации однородных массивов – это задание
начального адреса массива в базовом регистре Rn и индекса в индексном регистре Rm. В
зависимости от длины данных в массиве индекс должен масштабироваться (см.табл. 10.4).
Таблица 10.4 Масштабирование индекса в зависимости от формата данных
Формат данных в массиве

Offset

Байт
Полуслово
Слово
Длинное слово

i
2xi
4xi
8xi

196

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Для этого в архитектуре процессора предусмотрено использование специального
кольцевого сдвигового регистра, который может увеличивать значение в регистре Rm в 1,
2, 4, или в 8 раз.
Мнемоника настоящей базово-индексной адресации с авто-масштабированием
индекса выглядит как показано в табл. 10.5.
Таблица 10.5 Мнемоника базово-индексной адресации
Мнемоника

Способ адресации

Основное применение

[Rn, Rm LSL #n]

Базово-индексная по содержимому
базового регистра Rn и индексу в
регистре Rm с авто-масштабированием
индекса числом разрядов сдвига влево:
offset = i x 2n = (Rm) x 2n;
Rt ← [((Rn)+offset)]

Доступ к данным по начальному
адресу массива и индексу с
автоматическим
расчетом
смещения
в
зависимости
от
формата данных в массиве.

n=0,1,2 или 3

Рис. 10.5 можно использовать для иллюстрации этого метода адресации, заменив
выражение для вычисления эффективного адреса доступа к данным на следующее:
Adr = (Rn)+(Rm) x 2n.
Способ базово-индексной адресации элементов массива оптимален для
автоматизированной обработки данных в циклах – Вы просто должны инкрементировать
или декрементировать значение в индексном регистре при очередном доступе к данным,
смещение будет определено автоматически.

3)

4)

5)

6)

7)

1) Первый операнд после мнемокода любой команды загрузки/сохранения
– это регистр назначения Rt, приемник в команде LDR или передатчик в
команде STR.
2) Квадратные скобки «[ ]» являются признаком косвенной адресации.
Если внутри них содержится имя только базового регистра Rn, то он является
регистром-указателем адреса расположения данных (это соответствует классической
косвенной адресации).
Если после имени базового регистра Rn через запятую внутри квадратных скобок
указано смещение #offset, то адрес доступа к данным определяется суммой
содержимое базового регистра и величины смещения. Исходное содержимое регистрауказателя не модифицируется.
Если смещение указано через запятую после квадратных скобок – то это признак
адресации с пост-смещением указателя. В этом случае содержимое базового регистра
модифицируется сразу после доступа к данным.
Восклицательный знак «!» после квадратных скобок является признаком адресации с
пре-смещением указателя. В этом случае содержимое базового регистра
модифицируется на величину смещения перед доступом к данным.
Для обработки массивов данных одного формата в циклах используйте базовоиндексную адресацию с авторасчетом величины смещения по значению индекса и
формату данных в массиве.

197

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ

10.8 Загрузка и сохранение двойных слов
Для ускорения операций загрузки/сохранения данных используются операции с
двойными словами (64-бита), синтаксис которых представлен ниже:
ОpD{cond} Rt, Rt2, [Rn {, #offset}]
Способ адресации источника (LDR)/приемника (STR) данных в памяти
Rn – базовый регистр; #offset – непосредственное смещение
Rt2 – Регистр-приемник (LDR) или источник (STR) данных (второй)
Rt – Регистр-приемник (LDR) или источник (STR) данных (первый)
cond – Код условного выполнения команды
D – Двойного слова
Op - Операция
LDR – Загрузка двух регистров последовательными словами из памяти
STR - Сохранение содержимого двух регистров в последовательных ячейках памяти

На месте первого операнда через запятую указываются оба 32-разрядных регистраприемника или источника данных, которые образуют один 64-битовый регистр Rt, Rt2. К
основному мнемокоду команды добавляется суффикс «D». В командах двойной
загрузки/сохранения применяется только базовая адресация с непосредственным
смещением. Другие способы адресации недоступны.
1) Являются ли термины «косвенная адресация» и «базовая
адресация» синонимами?
2) Какой метод адресации лучше использовать при копировании
массива данных из ПЗУ в ОЗУ?
3) Какой формат данных содержится в массиве, если для его
обработки используется способ адресации [R0, R1 LSL #1]?
1) Термин «базовая» адресация несколько шире: базовая с
непосредственным смещением; с пре- или пост-смещением; базовоиндексная, в том числе с автомасштабированием индекса.
2) Наиболее удобен метод базовой адресации с пост-смещением
содержимого указателей [Rn], #offset, когда их содержимое будет
автоматически обновляться после каждой операции копирования единицы
информации на длину данных.
3) Массив содержит полуcлова, так как индекс в регистре R1 автоматически
удваивается перед доступом к данным.

10.9 Технология организации циклов при работе с
массивами данных
Большие объемы данных (массивы, структуры и т.д.), как правило,
обрабатываются автоматически с использованием циклов. Прежде чем
привести примеры подпрограмм работы с массивами данных в памяти,
рассмотрим наиболее общие принципы организации циклов на языке
Ассемблер. В простейшем цикле заданное программистом число данных
обрабатывается единообразно (по одному и тому же алгоритму). В цикле
всегда имеется:
198

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ






Заголовок цикла, в котором задается число проходов цикла путем инициализации
переменной, называемой счетчиком числа циклов, устанавливаются начальные
значения других переменных, используемых в теле цикла, в том числе регистровуказателей, адресующих память.
Тело цикла - последовательность команд, которая повторяется при каждом проходе
цикла, обеспечивая, в том числе, авто-модификацию указателей для обращения к
очередным данным в памяти и дополнительные команды модернизации значения в
счетчике числа циклов (инкрементирования или декрементирования);
Окончание цикла – команда или последовательность команд, в которой проверяется
условие выхода из цикла. Если оно выполняется – организуется выход из цикла. Если
нет – то тело цикла выполняется вновь. При этом все подготовительные операции по
доступу к очередным данным должны быть выполнены заранее, в теле цикла во время
предыдущего прохода или в заголовке цикла.
Возможны две технологии организации циклов – рис. 10.6.
Цикл

Цикл
Заголовок цикла:
Инициализация:
• Переменных.
• Счетчика числа циклов;

Заголовок цикла:
Инициализация:
• Переменных.
• Счетчика числа циклов;

Тело цикла:
• Многократно выполняемая
последовательность
команд;
• Модернизация переменных
• Инкременирование/
Декрементирование
счетчика числа циклов;

Нет

Окончание цикла
Условие выхода из цикла
выполняется?

Да

Окончание цикла
Условие выхода из цикла
выполняется?
Нет
Тело цикла:
• Многократно выполняемая
последовательность
команд;
• Модернизация переменных
• Инкременирование/
Декрементирование
счетчика числа циклов;

Да
Продолжение программы

Продолжение программы

а)

б)

Рис. 10.6 Технологии организации циклов на Ассемблере
Они отличаются порядком расположения блока проверки условия выхода из цикла
по отношению к блоку тела цикла. В первом случае этот блок располагается после тела
цикла, поэтому цикл выполняется, по крайней мере, один раз, после чего следует проверка
условия выхода из цикла. Во втором случае условие выхода из цикла проверяется
непосредственно перед телом цикла, и поэтому цикл может вообще ни разу не
выполниться. В первом случае требуется только один условный переход, причем в начало
цикла, если условие выхода из цикла ложное, а во втором – два: один условный переход
вперед в обход тела цикла, если условие выхода из цикла истинно, и второй, безусловный
переход в начало цикла для очередной проверки условия выхода из цикла.

199

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
10.9.1.1 Цикл с пост-проверкой условия выхода
Счетчик числа циклов в теле цикла может обрабатываться по-разному: или
декрементироваться или инкрементироваться. В первом случае в блоке проверки условия
выхода из цикла значение счетчика проверяется на ноль, а во втором – на достижение
заданного числа проходов. Операция декрементирования может сопровождаться попутной
установкой флагов результатов операции и после нее можно сразу использовать команду
условной передачи управления по «не нулю» в начало цикла, без применения
дополнительной команды сравнения. Операция инкрементирования потребует
дополнительной команды сравнения текущего числа проходов с заданным. Сравните
приведенные ниже два варианта структур цикла:
Вариант № 1 – Декрементирование счетчика числа циклов и использование для
перехода в начало цикла команды условной передачи управления BNE
; Определить переменную – «Число проходов цикла»
N
EQU 10
; Инициализировать счетчик числа циклов
MOV R0, #N
Loop
; Тело цикла
;

; Декрементировать счетчик числа циклов
SUBS R0, #1
; «S» - Установить флаги
; Повторить цикл, если значение счетчика циклов не равно нулю
BNZ Loop
; В противном случае, выйти из цикла и продолжить выполнение
; программы
Next
;

Вариант № 2 – Инкрементирование счетчика числа циклов и использование
дополнительной команды сравнения CMP для проверки условия выхода из цикла
; Определить переменную«Число проходов цикла»
N
EQU 10
; Обнулить счетчик числа выполненных циклов
MOV R0, #0
Loop
; Тело цикла
;

; Инкрементировать счетчик числа выполненных циклов
ADD R0, #1
; Сравнить число проходов с заданным
CMP R0, #N
; Повторить цикл, если заданное число проходов не достигнуто
BNE Loop
; Выйти из цикла и продолжить выполнение программы
Next
;


200

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
1) Сколько раз будет выполняться цикл в Вариантах № 1 и № 2, если
N=10?
2) Сколько раз будет выполняться цикл в Вариантах № 1 и № 2, если
N=0?
3) Можно ли заменить в окончании цикла для варианта № 2 условие «Не
эквивалентно» (NE) на условие «Строго меньше для сравнения чисел без знака» (LO),
на условие «Меньше или равно для сравнения чисел без знака» (LS)?
1) И в том и в другом случае – 10 раз.
2) Первая операция декрементирования счетчика числа циклов в
варианте № 1 приведет к установке в регистре R0 значения 4 294 907 295
(максимально возможного целого 32-разрядного числа). Следовательно,
всего будет выполнено до обнуления счетчика 4 294 907 296 проходов.
Это эквивалентно фактически «бесконечному циклу». В варианте № 2 с каждым
проходом тела цикла значение счетчика числа циклов будет увеличиваться на 1 и при
выполнении 10 проходов условие выхода из цикла станет истинным. Будьте
внимательны с заданием начальных значений в счетчике числа циклов!
3) На условие «LO» – да, на условие «LS» – нет. В последнем случае число
выполненных проходов цикла увеличится на 1 по сравнению с заданным.
Принципиальной разницы в этих двух вариантах организации циклов
нет, однако предпочтение все же следует отдавать первому варианту, так как
при прочих равных условиях он чуть быстрее и проще для понимания.
10.9.1.2 Цикл с пре-проверкой условия выхода
Как уже отмечалось, в этом случае тело цикла может и не быть выполненным. В
системе команд процессоров Cortex-M имеются две специальные команды, облегчающие
реализацию циклов с пре-проверкой условия выхода из цикла:
CBZ Rn, label

- Compare and branch if zero
Сравнение и переход, если ноль
CBNZ Rn, label - Compare and branch if non-zero
Сравнение и переход, если не ноль
Они выполняют два действия одновременно: проверяют текущее значение в
регистре-источнике Rn на ноль и в случае выполнения (Z) или не выполнения (NZ) этого
условия обеспечивают передачу управления вперед на указанную в команде метку.
Обратите внимание на подчеркнутое выше слово – «вперед». Передача управления назад,
в сторону уменьшающихся адресов, т.е. в сторону начала цикла – не поддерживается.
Следовательно, эти команды можно использовать для организации досрочного выхода из
цикла или для условных переходов вперед внутри тела цикла. Они оптимальны и для
организации циклов с пре-проверкой условия выхода из цикла, как показано ниже:

201

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Вариант № 3 – Декрементирование счетчика числа циклов при каждом проходе и
проверка условия выхода из цикла перед телом цикла командой СBZ
; Инициализация счетчика числа циклов
MOV R0, #N
;

; Проверить условие выхода из цикла
Cange_Exit
CBZ R0, Next
; Тело цикла
;

; Декрементировать счетчик числа циклов
SUB R0, #1
; Возврат управления в блок проверки условия выхода из цикла
B Cange_Exit
Next
;

Заметьте, что команды CBZ, CBNZ сами тестируют содержимое регистраисточника на ноль. Поэтому специально выставлять флаги с помощью суффикса «S», как
в команде SUB не нужно.
Мы рассмотрели реализацию простых условий выхода из цикла по
текущему состоянию специальной переменной-счетчика числа циклов. В
общем случае выход из цикла выполняется по результату сравнения любой
переменной с другой переменной или с заданным значением. Условием
выхода из цикла может быть также проверка на истинность любого
логического выражения. Циклы с пост-проверкой условия выхода аналогичны операторам
языков высокого уровня типа Repeat … Until – «Повторять, пока логическое
условие не будет истинным», а циклы с пре-проверкой условия операторам Wihle
Do… – «Пока логическое условие истинно, выполнять» (более подробно в главе 15).

10.10
Разработка подпрограмм копирования данных в
памяти
Одной из наиболее часто решаемых на практике задач является задача
копирования массива констант из ПЗУ в ОЗУ микропроцессорной системы.
Она может возникнуть, например, при инициализации значений переменных в
ОЗУ.
При
загрузке
рабочей
программы
начальные
значения
инициализируемых переменных сначала загружаются в ПЗУ вместе с кодом программы, а
затем, перед выполнением программы, копируются в нужные области оперативной
памяти. При разработке программ на С/С++ эту функцию выполняют специальные
подпрограммы в стартовом файле проекта.
Возьмем за основу один из предыдущих проектов, в которых в конце рабочей
программы была выполнена инициализация таблицы констант в кодовой секции, и
изменим основную программу так, чтобы она вызывала подпрограмму, выполняющую
копирование массива слов заданной длины из ПЗУ в ОЗУ – проект CPU_6 (MyProg_6.s).
Прежде всего договоримся, какие параметры будем передавать в подпрограмму по имени
Copy_Rom_Ram. Очевидно, что это должны быть три параметра: начальный адрес
массива констант в ПЗУ (A_Rom); начальный адрес массива переменных в ОЗУ (A_Ram)
и длина массива в количестве 32-разрядных слов (N_32).

202

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Последний параметр зависит исключительно от приложения и может быть
установлен в начале программы
директивой
определения
символической переменной EQU.
Значение первого параметра
(A_ROM)
будет
автоматически
определено при резервировании и
инициализации констант в конце
нашей программы – это адрес
фактического размещения таблицы констант в памяти.
Значение второго параметра
(A_RAM) будет определено при
резервировании заданного объема ОЗУ
под переменные проекта, что нужно
сделать, например, сразу после
кодовой секции, объявив секцию данных MyData. Директива SPACE резервирует нужное
число слов в памяти данных, начиная с адреса A_RAM, фактическое значение которого
будет известно только после компоновки проекта. Заметьте, что для определения объема
резервируемой области памяти мы использовали арифметическое выражение (4*N_32), в
котором символ N_32 уже определен нами в начале программы.
Итак,
создадим
заголовок
подпрограммы. Это комментарий,
который
содержит
название
подпрограммы, перечень входных
параметров, передаваемых ей из
основной программы содержимым
регистров ЦПУ с указанием их имен,
перечень используемых в подпрограмме регистров (значения которых могут «портиться»
при работе программы). Если подпрограмма возвращает значение в основную программу
– отмечается, каким образом, например, содержимым одного из регистров. В нашем
случае подпрограмма лишь выполняет копирование данных из одного места памяти в
другое.
Существуют разные способы передачи параметров в подпрограммы и возврата из
них результата, среди которых один из самых простых и поэтому чаще используемых –
значениями регистров ЦПУ. Для удобства пользователей фирма ARM разработала
стандарт передачи параметров в подпрограммы, в котором, в частности, рекомендуется
использовать для этой цели регистры младшего регистрового банка (желательно r0-r3). В
заголовке подпрограммы может указываться и другая информация, вплоть до фамилии
разработчика, даты создания подпрограммы и даты последнего обновления.
Где размещается подпрограмма?
Обычно в конце основной программы, в
текущей
кодовой
секции,
перед
константами и таблицами констант,
если они есть. Она представляет собой
цикл, в котором слово считывается из
массива-источника
в
регистр
временного хранения данных r3 и тут
же сохраняется в массиве-приемнике.
Для этого применяется базовая адресация с пост-смещением указателя.
Оба регистра указателя r0 и r1 автоматически получают приращение +4 для
доступа к очередной ячейке памяти массива-источника и приемника в следующем
203

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
«проходе тела» цикла. Счетчик числа циклов (регистр r2) декрементируется, и результат
проверяется на ноль командой сравнения. Для выработки флагов можно использовать
также команду SUB с установленной опцией «S» – SUBS. В последнем случае команда
сравнения не нужна. Если не все слова скопированы – цикл повторяется. Для этого
используется команда условной передачи управления по признаку «Не эквивалентно»
(NE). В противном случае выполняется возврат в основную программу (содержимое
регистра связи LR загружается в счетчик команд PC).
Таким образом, подпрограмма может
скопировать нужное число слов из одного
места памяти в другое (не обязательно из ПЗУ
в ОЗУ, но и из одной области ОЗУ в другую).
Вернемся к основной программе и покажем,
как выполняется передача параметров в
подпрограмму. Для передачи первого параметра мы применили псевдокоманду
Ассемблера «Загрузить адрес данных по их метке» ADR, для второго параметра – псевдокоманду загрузки регистра 32-разрядным числом с одновременным созданием
литерального пула, содержащего этот адрес, а для третьего параметра – обычную команду
MOV с непосредственной адресацией операнда. Дело в том, что данные в ПЗУ
расположены «близко» и команда относительной адресации по текущему содержимому
счетчика команд ADR может быть сгенерирована. Область ОЗУ начинается в
соответствии с картой памяти с адреса 0x20000000. Это «длинный адрес», использование
для загрузки которого командой ADR вызовет ошибку. Приходится генерировать
команду, которая будет использовать копию «длинного адреса» в литеральном пуле.
Байтовые константы, напротив, можно загрузить и простой командой пересылки.
Далее следует вызов подпрограммы. После возврата основная программа
зацикливается для удобства отладки. Естественно, что эту подпрограмму можно вызвать
из основной программы произвольное число раз с нужными параметрами.
Выполните компиляцию и компоновку проекта CPU_6. Загрузите программу в
отладчик.

10.11

Как открыть окно содержимого памяти?

При работе с памятью целесообразно окрыть окно содержимого
памяти (дампа памяти) для наблюдения за
результатом работы программы. Сделать это
можно, находясь в отладчике, в меню «View»
(«Просмотр»), выбрав одну из четырех возможных областей памяти, например, Memory 1.
Сложность может быть в том, что Вы не знаете реального начального адреса области ОЗУ.

Загляните в карту загрузки CPU_6.map, созданную компоновщиком, и найдите в
ней значение символа A_RAM. В фрагменте этого файла Вы видите, что секция данных
MyData начинается с адреса 0x20000000 и занимает в памяти всего 4 слова. Дальше
зарезервировано место для стека (1 Кбайт), который пока не используется. Следовательно,
для наблюдения за процессом копированием данных в ОЗУ нужно открыть окно памяти с
начальным адресом 0x20000000.

204

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Проследите за выполнением программы в регистровом окне и в окне
дампа памяти в пошаговом режиме работы и в режиме прогона. Обратите
внимание, что адрес возврата при обращении к подпрограмме сохраняется в
регистре связи LR, а при возврате из нее восстанавливается из регистра LR в
счетчик команд PC. Содержимое окна памяти доказывает, что программа работает.
Цветом выделены байты памяти, содержимое которых при выполнении программы
изменилось. Все четыре слова скопированы в ОЗУ, что и требовалось.

3)
4)

5)

6)

1) Модифицируйте программу и подпрограмму так, чтобы данные,
расположенные в ПЗУ, копировались по полуслову. Выполните отладку.
Результат копирования такой же или отличается?
2) Сделайте то же самое, но для копирования массива по байтам.
Выполните отладку.
Так какая же из трех подпрограмм лучше?
В нашей подпрограмме выполняются «как бы лишние» операции пост-смещения
указателей. Можно ли написать подпрограмму так, чтобы значения указателей не
выходили за пределы зарезервированной для массивов памяти. Попробуйте.
Оставьте те же директивы резервирования констант в кодовой памяти. Проверьте,
как работает команда двойной загрузки слов в регистры ЦПУ, сложите первые две
константы Const_0 и Сonst_1 после загрузки. Оцените результат.
Зарезервируйте в кодовой секции строку символов “Privet ARM Cortex-M4F CPU” c
использованием директивы DCB. В качестве признака конца строки (прерывателя
строки) используйте нулевой байт 0. Напишите подпрограмму копирования в ОЗУ
строки символов любой длины, кончающейся «прерывателем». Проверьте ее
работу в отладчике.
1) В
подпрограмме
необходимо
заменить
команды
загрузки/сохранения слов на команды загрузки/сохранения полуслов с
использованием базовой адресации с пост- смещением содержимого
указателя.
Естественно,
число проходов цикла
необходимо
увеличить
вдвое:
MOV r2, #(N_32*2),
чтобы скопировать все данные в ПЗУ. Результат будет точно таким же.
Модифицированный файл MyProg_6_1.s.

2) Изменения в подпрограмме касаются использования команд загрузки/сохранения
байт с базовой адресацией и постсмещением,
а
также
с
увеличением числа проходов
цикла в основной программе (MOV r2, #(N_32*4)). Результат копирования
тот же. (см. MyProg_6_2.s).

205

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
3) Ответ зависит от того, что за данные находятся в исходном массиве. Если они
однородные и их число кратно 4-м, то разумнее использовать самую быструю
процедуру копирования слов. Если данные – байты или полуслова, число которых
кратно 2-м, то процедуру копирования полуслов. В общем случае, при неизвестных
заранее объемах пересылки используется побайтовое копирование.
4) Да. Придется изменить логику
организации цикла: тестировать
счетчик числа циклов не в конце,
а
перед
телом
цикла

использовать технологию препроверки условия выхода из
цикла.
(MyProg_6_3.s).
Если
счетчик
будет
обнулен,
достаточно просто выйти из
подпрограммы, используя для
этого команду условного возврата в основную программу по нулю EQ
(«Эквивалентно») – BXEQ lr. Для проверки состояния счетчика можно выполнить
пересылку данных из него (r2) в регистр временного хранения информации r3 с
опцией установки флагов результатов операции MOVS. Как видите, если начальное
значение счетчика циклов равно нулю, то тело цикла не выполняется ни разу и
сразу следует возврат в основную программу.
5) Обратитесь к файлу MyProg_6_4.s, содержащему пример решения задачи. Сначала
выполняется
инициализация
базового регистра r0 начальным
адресом области ПЗУ, содержащей
таблицу
констант.
Далее
командой двойной загрузки слов LDRD в регистры назначения r1, r2 выполняется
считывание сразу двух констант Const_0 и Const_1, которые расположены в памяти
по последовательным адресам. Теперь достаточно выполнить трех-операндную
команду сложения с сохранением результата в регистре r3. Результат работы
программы показывает, что загрузка данных и их обработка
выполнены правильно.

206

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
6) Каждый символ имеет свой код ASCII (американский стандарт представления
символьной информации в компьютерах) и хранится в памяти в виде байта. Строка
символов выглядит в памяти как последовательность байт – ASCII-кодов
соответствующих символов. Закодировать отдельный символ можно, поместив его
в одиночные кавычки, а строку
символов – в двойные кавычки.
Для этой цели используется
директива
резервирования
и
инициализации
байт
DCB.
Обратите внимание на то, что в
конце последовательности кодов
символов мы разместили байтпризнак конца строки. Это
сделано для того, чтобы в
процессе
посимвольного
копирования строки, прекратить
копирование после обнаружения
кода
«конца
строки».
В
подпрограмме используется постпроверка условия выхода из
цикла. Для этого код текущего
символа сравнивается с кодом
конца строки (0). Если конец
строки достигнут – выполняется возврат в основную программу. В
процессе отладки подпрограммы проследите за формированием
флага Z (нулевого результата). Для этого раскройте содержимое
регистра статуса программы пользователя xPSR в окне регистров.
Пример программы приложения –в файле MyProg_6_5.s.

10.12
Как управлять форматом выдачи данных в окно
памяти при отладке?
Находясь в окне дампа памяти, Вы можете воспользоваться
контекстно-зависимым меню и изменить представление данных в
памяти для удобства анализа расположенных в ней данных. Так, при
выполнении последнего задания целесообразно открыть окно памяти,
начиная с адреса 0x20000000 (начала секции данных в ОЗУ) и вызвать
это меню правой клавишей мыши. Теперь можно изменить формат
представления данных в окне, выбрав обычное десятичное
представление
(если
опция
Decimal
установлена),
или
шестнадцатеричное (если сброшена – нет «галочки» в позиции
Decimal),
Знаковое
(Signed)
или
беззнаковое
(Unsigned)
представление. В средней части меню можно выбрать особые
форматы представления данных: в виде ASCII-символов (Ascii) текстового сообщения; в виде 32-разрядных чисел в формате с
плавающей точкой однократной (Float) или двойной точности (Double).

207

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
Выберите для приложения MyProg_6_5.s представление информации в памяти в
виде символов (Ascii). Тогда, при завершении программы Вы получите в окне дампа
памяти соответствующее текстовое сообщение, как
при выводе текста на экран компьютера. Оно
свидетельствует о том, что заданная в ПЗУ строка
символов скопирована правильно. К сожалению, в
это окно возможен вывод только латинских
символов (кириллица не поддерживается). Вывод строковых сообщений в окно дампа
памяти помогает в отладке сложных программ, визуализируя этот процесс.
Обозначения форматов данных в Ассемблере и в С/С++ несколько отличаются.
Cреда разработки μVision поддерживает обозначения, принятые в СИ: символ, короткое
целое, целое, длинное целое. В таблице 10.6 показано соответствие этих форматов в СИ и
Ассемблере (в скобках), а также взаимное расположение данных: каждое короткое целое
Short (Полуслово) состоит из двух символов Char (Байт); каждое целое Int (Слово) из двух
коротких целых (полуслов); каждое длинное целое Long (Длинное слово) из двух
обычных 32-разрядных слов:
Таблица 10.6 Возможные форматы вывода данных в окно дампа памяти
Char
Char
Char
Char
(Байт)
(Байт)
(Байт)
(Байт)
Short (Полуслово -16 р.) Short (Полуслово -16 р.)
Int (Слово – 32 р.)
Long (Длинное слово – 64 р.)

Char
Char
(Байт)
(Байт)
Short (Полуслово -16 р.)
Int (Слово – 32 р.)

Char
Char
(Байт)
(Байт)
Short (Полуслово -16 р.)

Среда μVision позволяет выбрать для дампа памяти
наиболее удобный формат представления данных. Для этого
нужно всего лишь поставить «галочку» рядом с
обозначением соответствующего формата. Данные могут
быть представлены как числа без знака, так и со знаком.
Рассмотрим варианты вывода данных для нашей конкретной программы.
Используем Hex-формат:
1). Представление в виде символов без знака: Unsigned – Char (байтами):

2). Представление в виде коротких целых без знака: Unsigned – Short
(полусловами):

3). Представление в виде целых без знака: Unsigned – Int (32-разрядными словами):

Первое представление позволяет проанализировать ASCII-коды использованных в
строке символов. Например, символу пробела соответствует код 0x20, символу ‘e’ – код
0x65, символу ‘M’ – код 0x4D и т.д. Во втором представлении каждая пара байтов
208

ГЛАВА 10. ДОСТУП К ДАННЫМ В РЕГИСТАХ ЦПУ И ПАМЯТИ
объединяется в полуслово, и значения полуслов выводятся в удобном для программиста
виде – сначала старший байт, затем младший (реально данные располагаются в памяти,
начиная с младшего байта). В третьем варианте каждые четыре байта объединяются в
одно 32-разрядное слово, которое также представляется в удобном для анализа виде.
Если Вам не удобен Hex-код, выберите десятичный формат
представления данных. Таким образом, среда μVision может выполнять
функции встроенного калькулятора, быстро преобразуя данные в памяти из
одного формата в другой (Hex ↔ Dec), в том числе из целочисленного
формата в формат с плавающей точкой и обратно, что часто требуется в
процессе отладки. Контекстное меню окна памяти позволяет также оперативно
модифицировать значения в ячейках памяти и устанавливать точки останова. Для
изменения содержимого ячейки памяти достаточно щелкнуть правой клавишей мыши на
содержимом этой ячейки (она будет выделена пунктирной рамкой) вызвать контекстнозависимое меню правой клавишей мыши, в котором выбрать
команду модификации содержимого этой ячейки. Появится
окно со строкой ввода и примерами ввода данных. Данные
можно вводить цифрами, символами, строкой символов или
последовательностью цифр, разделенных запятой. При
множественном вводе данные будут вводиться в память
последовательно, начиная с указанного адреса.
Более простой метод ввода значений в память состоит в том, чтобы дважды
щелкнуть на содержимом ячейки памяти левой клавишей мыши. Ее содержимое будет
выделено фоном, и Вы сможете модифицировать его, вводя нужную последовательность
цифр, заканчивая ввод клавишей «Enter». Ограничение метода состоит в том, что ввод
возможен только в текущем формате, например, Heх-цифрами, если соответствующее
представление данных установлено. Таким образом корректируется значение только в
одной ячейке памяти.
1) Поэкспериментируйте с выводом информации в окно дампа памяти в
различных форматах, например, в формате Char (байта) в десятичном коде.
2) Попробуйте модифицировать одну ячейку памяти и целую область,
начиная с определенного адреса.
3) Проверьте, правильно ли вводятся отрицательные числа? Убедитесь
в этом, изменив формат ввода на Hex-формат.
4) Проверьте, как работает технология модификации текущего значения в одной
ячейке памяти по двойному щелчку мыши.
Среда μVision предоставляет удобные инструменты для начальной
инициализации значений переменных или массивов в любых областях
памяти, а также для их модификации в процессе отладки. При этом данные
могут вводиться и отображаться в наиболее удобной для программиста
форме.

209

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ

11 АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
Оглавление
11.1. Команды сложения и вычитания слов ............................................................................ 211
11.2. Технология отладки программ с точками останова ...................................................... 213
11.3. Использование командного окна среды μVision ........................................................... 215
11.3.1. Командное окно ........................................................................................................ 215
11.3.1.1. Вывод на дисплей текущего содержимого регистров. ................................. 215
11.3.1.2. Как модифицировать значение регистра или переменной? ......................... 216
11.3.1.3. Как изменить систему счисления для выдачи информации на дисплей? ... 216
11.3.1.4. Как использовать командное окно для модификации содержимого памяти?
..................................................................................................................................................... 216
11.3.2. Окна наблюдаемых переменных. Использование в процессе отладки. .............. 217
11.4. Арифметические операции с непосредственными операндами .................................. 218
11.5 Арифметические операции с массивами данных в памяти ........................................... 220
11.5.1. Обработка массивов 32-разрядных слов ................................................................ 220
11.5.2. Арифметические операции с массивами байт и полуслов ................................... 222
11.6. Многословная арифметика .............................................................................................. 223
11.7. Макроопределение. Макровызов. Использование макробиблиотек ........................... 224
11.7.1. Общие положения .................................................................................................... 224
11.7.2. Примеры макроопределений суммы и разности двойных слов ........................... 225
11.7.3. Как вызываются макрокоманды? ............................................................................ 225
11.7.4. Как использовать макробиблиотеки? ..................................................................... 226
11.8. Команды умножения и деления ...................................................................................... 229
11.9. Команды множественной загрузки/сохранения регистров .......................................... 231
11.9.1 Назначение и области применения. ......................................................................... 231
11.9.2. Синтаксис основных команд множественной загрузки/сохранения регистров . 231
11.9.3. Использование операций множественной загрузки/сохранения регистров для
отладки арифметических алгоритмов ..................................................................................... 233
11.9.3.1. Вычисления с 32-разрядными числами без знака ......................................... 233
11.9.3.2. Вычисления с 32-разрядными числами со знаком в дополнительном коде
..................................................................................................................................................... 235
11.10. Пример использования команд «длинного» умножения ............................................ 236

210

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
Арифметические операции являются основными, определяющими
вычислительную производительность процессора. В процессорах ARM,
кроме традиционных для микроконтроллеров команд сложения и
вычитания, имеются также команды умножения и деления. Этот набор
перекрывает весь спектр операций, необходимых для эффективных
вычислений. Так как ядро процессора 32-разрядное, большинство
операций выполняется над словами, хотя, как Вы уже заметили в предыдущей главе,
процессор без труда может обрабатывать и данные меньшего формата – полуслова и
байты. При этом загрузка полуслов и байт на обработку из памяти сопровождается
автоматическим преобразованием их формата к формату слова. Это преобразование
выполняется по-разному для чисел без знака и со знаком в дополнительном коде. Так как
разрядная сетка ЦПУ 32-разрядная, параллельно в АЛУ могут обрабатываться и два
полуслова и 4 байта, входящих в слово. При этом, правда, необходимо соблюдать
определенную осторожность, так как обычные флаги результатов операций не
формируются, хотя и имеются дополнительные возможности определения
промежуточных переполнений (см. приложение 1).

11.1 Команды сложения и вычитания слов
Синтаксис 32-разрядных операций сложения и вычитания:
ADD{S}{cond} {Rd}, Rn, Op2
ADC{S}{cond} {Rd}, Rn, Op2
SUB{S}{cond} {Rd}, Rn, Op2
SBC{S}{cond} {Rd}, Rn, Op2
RSB{S}{cond} {Rd}, Rn, Op2
Второй операнд-источник: 1) #imm; 2) Rm; 3) Rm, Shift
Первый операнд-источник
Регистр – приемник (опция)
Код условного выполнения команды (опция)
S (SET) – Опция установки флагов результатов операции: N, Z, C, V
ADD – (ADD) - Сложить 32-разрядные операнды:
Rd ← Rn + Op2
ADC – (ADD with Carry) - Сложить с учетом переноса:
Rd ← Rn + Op2 + Carry
SUB – (SUB) - Вычесть 32-разрядне операнды:
Rd ← Rn - Op2
SBC – (SUB with Carry) - Вычесть с учетом флага С (Заема): Rd ← Rn + Op2 - /Carry
RSB – (Reverse Subtrucnt) - Реверсивное вычитание:
Rd ← Op2 - Rn

Все команды могут быть трехоперандными, причем в качестве второго операнда источника можно использовать универсальный второй операнд: непосредственную
константу, регистр ЦПУ или регистр ЦПУ, содержимое которого подвергается
предварительной обработке в кольцевом сдвиговом регистре процессора, например,
масштабированию. Имя регистра-приемника является опциональным. Если оно опущено,
то функцию приемника выполняет первый операнд-источник Rn – команда становится
двухоперандной.
Для расширения эффективной разрядности процессора при обработке данных (в
операциях многословной арифметики) предусмотрены команды сложения с учетом ранее
возникшего переноса C (Carry) и вычитания с учетом ранее возникшего «заема». Его
функцию в процессорах ARM также выполняет флаг С: он сбрасывается, если при
вычитании был «заем» и выставляется – если «заема» не было. Можно считать, что флаг
Carry является инверсией флага «заема» Borrow при вычитании.
В общем случае флаги результатов операций не модифицируются. Для того, чтобы
они были выработаны, необходимо использовать команды с дополнительным суффиксом
211

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
«S» - установки флагов. При этом будут сформированы флаги N, Z, C, а также флаг
«знакового переполнения» V.
Интерес представляет команда реверсивного (обратного) вычитания, когда из
значения второго операнда вычитается значение первого. Она находит широкое
применение, например, для инвертирования знака числа: RSB r1, #0. Из нуля вычитается
содержимое регистра r1 и сохраняется в том же регистре. Следовательно, это команда
инвертирования знака числа, расположенного в регистре r1.
В конце предыдущей главы мы рассмотрели основные приемы
наблюдения за содержимым памяти в среде μVision, которые позволяют
исследовать это содержимое, рассматривая его как данные в любом формате
(числа со знаком или без знака). Содержимое памяти может быть
представлено в десятичной или шестнадцатеричной системе счисления. Кроме того, при
отладке программист может быстро ввести в память любые исходные данные и на этом
наборе переменных проверить работу программы. Фактически мы имеем встроенный
«преобразователь» типовых форматов данных в памяти. Рекомендуем активно
пользоваться этой возможностью, в том числе для отладки программ, содержащих
вычислительные блоки.
Покажем, как это делать, на примере
проекта CPU_7. В конце программы
приложения MyProg_7.s объявляется секция
данных и в ней резервируется массив из 4-х
слов Array_W. Он будет содержать исходные
данные, которые мы можем ввести, используя
окно дампа памяти, непосредственно перед
выполнением программы в отладчике.
Будем исследовать операции сложения
и вычитания слов, поэтому зарезервируем
еще два слова для сохранения в памяти
результатов
попарного
суммирования
исходных слов массива – Sum_W и Sub_W –
для сохранения результатов попарного
вычитания исходных слов. Сама программа
выполняет установку указателей в регистрах
r0, r4 и r5 на начальные адреса этих областей
памяти, а затем, с использованием команд
загрузки и сохранения данных в памяти
(базовая адресация с пост-смещением и
обычная косвенная адресация) выполняет
попарный расчет сумм и разностей слов,
находящихся в массиве Array_W. При этом
слова сначала загружаются в регистры r1, r2,
а затем выполняется расчет сумм и
разностей с сохранением результатов в
памяти.

212

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
Обратите внимание на то, что начальные адреса областей ОЗУ объявлены
глобальными переменными (директива EXPORT). Это сделано для того, чтобы они
попали в таблицу символов проекта CPU_7.map, и мы узнали фактическое размещение
переменных в памяти. После «сборки» проекта нужно открыть окно памяти, начиная с
адреса 0x20000000 (см.
файл CPU_7.map).
Первые четыре слова – массив исходных данных, два следующих слова –
результаты попарного суммирования, следующие два слова – результаты попарного
вычитания исходных операндов.
Представим исходные данные и результаты расчетов в удобной для наблюдения
десятичной системе счисления в формате знаковых длинных целых чисел (Decimal,
Signed, Int) слов со знаком. Загрузим программу в отладчик, откроем окно дампа памяти и
заполним первые четыре слова произвольными десятичными кодами. Выполним
программу в пошаговом режиме и проследим за тем, как исходные данные
последовательно считываются из памяти: при каждом доступе к очередной ячейке памяти
ее содержимое выделяется красным цветом. При обращении к памяти по записи результат
записи выделяется в окне памяти салатовым цветом. Убедитесь, что обе суммы и обе
разности рассчитаны верно.

11.2 Технология отладки программ с точками останова
Интегрированная среда разработки μVision предоставляет
широкие возможности по отладке программ. Наиболее часто
используется выполнение программы в режиме «прогона» (RUN) и
пошаговое выполнение. Одним из очень эффективных средств является
также отладка программы с точками останова. Точкой останова в простейшем случае
является метка какой-то команды (адрес ее расположения), при достижении которой в
режиме прогона следует останов программы, чтобы программист смог исследовать
промежуточные результаты в регистрах процессора и памяти и убедиться в правильности
выполнения определенного фрагмента программы. Если все правильно, можно выполнить
программу в режиме прогона до следующей точки останова и убедиться в правильности
работы очередного фрагмента. Таким образом, отладка с точками останова – это
поблочная отладка программы с промежуточным контролем результатов. В общем случае
точки останова могут быть и более сложными, например, по доступу процессора к
указанному адресу в памяти. В этом случае прерывание наступит тогда, когда процессор
обратится к памяти по заданному адресу.
Установка и удаление простых точек останова
выполняется командами из меню Debug (Отладка) или
соответствующими пиктограммами на панели инструментов.
Первая команда позволяет установить/удалить точку
останова, вторая – разрешить/запретить ее использование (активировать/деактивировать),
третья – запретить все точки останова, четвертая – удалить все точки останова.

213

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
Технология установки точки останова:
1) находясь в режиме отладки, щелкните мышью в окне программы пользователя
в соответствующей строке программы – эта строка будет выделена голубым
треугольником в начале строки;
2) выполните команду установить точку останова – рядом с треугольником
появится красный «кружок» (символ точки останова).
Можете выполнить программу в режиме прогона. При
достижении точки останова произойдет автоматический
останов.
Допускается устанавливать любое число точек останова. Точку останова по
доступу к конкретной ячейке памяти можно установить из контекстного меню окна
памяти. Для этого достаточно щелкнуть левой клавишей мыши на содержимом нужной
ячейки (она будет выделена пунктирным прямоугольником) и вызвать контекстное меню
правой клавишей мыши. В этом меню имеется команда «Set Breakpoint at …»
«Установить точку останова» по текущему адресу.
1) Модернизируйте программу так, чтобы по результатам операций
сложения и вычитания вырабатывались флаги N, Z, C, V.
2) Введите те же исходные данные в память, что и в примере выше.
Установите точки останова после выполнения каждой операции сохранения
результата арифметической операции в памяти. Раскройте в регистровом
окне содержимое регистра статуса программы PSR для наблюдения за состоянием
флагов. Выполните отладку программы по точкам останова с контролем состояния
флагов после каждой операции. Объясните принцип формирования флагов для
каждой арифметической операции.
3) Измените набор исходных данных таким образом, чтобы при первой операции возник
флаг знакового переполнения V. Проверьте в отладчике.
Необходимо добавить к мнемонике команд сложения и
вычитания
суффикс
«S»

установки флагов (MyProg_7_1.s).
2) Вид исходной программы после
установки
точек
останова
изменится. Точку останова желательно ставить на
следующей команде, после завершения очередного
вычисления и сохранения результата в памяти. При
этом можно будет одновременно просмотреть и
состояние флагов в окне регистров и результат расчета в памяти. Для тех же исходных
данных получим: флаг N вырабатывается только при получении отрицательного
результата; флаг Z всегда сброшен, так как все результаты – ненулевые; флаг С
возник как флаг переноса при сложении двух
Операция
Состояние флагов
чисел (0xFFFFFFFE и 0xFFFFFFFC) и как флаг
N
Z
C
V
отсутствия «заема» при вычитании; флаг 1 345+644=989 0
0
0
0
0
0
0
переполнения не вырабатывается, так как все 2 345-644=-299 1
3
-2+(-4)=-6
1
0
1
0
результаты находятся в допустимом диапазоне
4
-2- (-4)=+2
0
0
1
0
чисел со знаком в дополнительном коде.
3) Добавим к максимально возможному положительному числу (0x7FFFFFFF) еще одно
положительное (0x2) – результат выйдет за диапазон допустимых чисел со знаком в
1)

214

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
дополнительном коде (станет как бы отрицательным) – будут установлены и флаг N и
флаг V (переполнения). Убедитесь в этом.

11.3 Использование командного окна среды μVision и окна
наблюдаемых переменных при отладке программ
11.3.1 Командное окно
Интегрированная среда μVision имеет широкие возможности по
отладке программ, с которыми мы уже частично познакомились. Вы
знаете, как просматривать и модифицировать содержимое регистров
ЦПУ и содержимое памяти, выбирать в окне памяти наиболее удобную
форму представления данных. Сейчас мы рассмотрим некоторые, часто используемые при
отладке, возможности Командного окна (Command Window), которое предоставляет
программисту дополнительный набор команд по управлению отладкой. Открыть это окно,
как обычно, можно из меню View (Просмотр).
В нижней части окна Command имеется строка ввода команд управления отладкой,
начинающаяся с «промпта» >, как в операционной системе DOS. Это символ приглашения
пользователя к вводу команд управления отладкой. Среди множества команд выделим те,
которые целесообразно использовать начинающим программистам.
11.3.1.1 Вывод на дисплей текущего содержимого регистров и переменных. Функция
встроенного калькулятора.
Команда вывода на дисплей командного окна текущего значения регистра или
переменной – одна из самых простых. Все, что нужно сделать – это ввести в командной
строке имя регистра или переменной и нажать клавишу «Enter» (Ввод). Текущее
содержимое регистра или переменной будет выведено на дисплей командного окна в
системе счисления по умолчанию (возможны два варианта – в десятичной или в
шестнадцатеричной системе счисления).
В общем случае, можно ввести любое арифметическое выражение, в состав
которого могут входить также символические имена регистров процессора, и заставить
μVision вычислить это выражение – функция встроенного калькулятора. В примере ниже
(рис. 11.1) мы запросили вывод на дисплей окна «Command» текущих значений регистров
r0, r1, а затем двух арифметических выражений (r1+5) и (r1+0xFF).

Рис. 11.1 Окно программы μVision
В нижней части командного окна выводятся списки-подсказки команд управления
отладкой. Так, имеются команды: запретить/разрешить точку останова, удалить все точки
останова, вывести на дисплей листинг имеющихся точек останова и т.д. После того, как
Вы введете первую букву предполагаемой команды, строка подсказок изменится: в ней
автоматически будет показан список только тех команд, которые начинаются с этой

215

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
буквы. Используйте меню Help («Помощь») для того, чтобы познакомиться более
подробно с назначением и особенностями применения команд управления отладкой.
11.3.1.2 Как модифицировать значение регистра или переменной?
Для этого достаточно ввести в командной строке имя регистра/переменной, знак
равенства «=», а за ним требуемое значение. Примеры:
r1=78 ; r1=0xAB ; r1=55+0x45+r0
Ввод заканчивается нажатием клавиши «Enter». При вводе допустимо
использование арифметических выражений, то есть функция встроенного калькулятора
работает, как показано в третьем примере.
11.3.1.3 Как изменить систему счисления для выдачи информации на дисплей?
Среда μVision имеет встроенную переменную с символическим именем Radix
(«Основание системы счисления»), которая отвечает за систему счисления по
умолчанию. Именно она используется для вывода данных на дисплей командного окна, а
также в некоторые другие окна среды, например, в окно наблюдаемых переменных
(Watch Window).
Заметим, что это не касается вывода данных в регистровое окно, в котором данные
всегда представляются в Hex-формате, а также в окно дампа памяти, в котором, как мы
уже видели, они могут представляться во множестве форматов, задаваемых пользователем
в контекстно-зависимом меню этого окна.
Значение системной переменной Radix может быть либо 10 (десятичная система),
либо 16 (шестнадцатеричная система). Чтобы установить требуемый формат вывода,
нужно выполнить команду модификации значения системной переменной:
Radix=10 – установить десятичное представление данных (Dec);
Radix=16 – установить шестнадцатеричное представление данных (Hex);
Приведем пример вывода на дисплей и модификации
содержимого регистра r1: 1) вывод в формате по умолчанию (Hex); 2)
модификация с использованием встроенного калькулятора; 3) проверка
результата модификации; 4) смена системы счисления; 5) вывод
содержимого r1 в десятичной системе счисления. Все введенные
команды отображаются на дисплее и могут быть просмотрены с
использованием механизма прокрутки.
11.3.1.4 Как использовать командное окно для модификации содержимого памяти?
Установка начальных значений в определенных областях памяти – одна из
наиболее часто встречающихся задач при отладке программ. Это можно сделать двумя
путями:
1) Открыть окно памяти и выполнить инициализацию содержимого памяти с
использованием контекстно-зависимого меню окна. Это удобно, так как одновременно
позволяет установить нужный формат представления данных (см. 10.12).
2) Выполнить в командном окне команду «Enter» (Ввод данных).
Для второго варианта наберите сразу после «промта» в строке ввода первую букву
команды Enter «E». Подсказка в нижней строке будет содержать список возможных
команд, начинающихся с этой буквы.
В верхнем регистре набраны буквы для ввода
соответствующей команды. Из подсказки следует, что первого
символа «E» достаточно – нажимаем клавишу пробела для
продолжения диалога, строка подсказок изменяется:
Теперь нам предлагается указать тип данных, которые будут вводиться в память:
CHAR (символы) – байтовый ввод; SHORT – короткие 16-разрядные числа со знаком или
216

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
без знака (полуслова); INT – целые числа со знаком или без знака (слова); LONG –
длинные целые числа со знаком или без знака (двойные слова); FLOAT – числа в формате
с плавающей точкой однократной точности; DOUBLE – то же, но двойной точности.
Достаточно ввести первую букву типа вводимых данных, например, «S» и нажать
клавишу пробела для продолжения диалога. Следующая подсказка говорит о том, что
нужно ввести начальный адрес области памяти, знак
равенства и после него список выражений, разделенных
запятыми, значения которых будут записаны в нужную
область памяти.
Начальный адрес вводится исключительно в шестнадцатеричной системе
счисления, а далее, после знака равенства, вводитсяпоследовательность данных (уже в
удобной для Вас системе счисления).
Ввод данных завершается нажатием
клавиши «Enter», о чем свидетельствует
соответствующая подсказка.
Поэкспериментируйте с просмотром содержимого регистров ЦПУ, их
модификацией из командной строки, а также с командами ввода данных в
память. Не забывайте устанавливать в окне дампа памяти нужный формат
вывода информации. Только при этом Вы получите соответствие
выполненных Вами действий и результата, отображенного в окне памяти.

11.3.2 Окна наблюдаемых переменных. Использование в процессе отладки.
Вы наверняка заметили, что в регистровое окно (Registers Window)
информация всегда выдается в шестнадцатеричной системе счисления.
Для просмотра содержимого регистров в десятичной системе, конечно,
можно пользоваться командным окном, как описано выше. Однако, это
не очень удобно, так как приходится выполнять несколько команд просмотра
содержимого регистров последовательно.
Есть и другой путь. Среда μVision позволяет открывать окна наблюдаемых
переменных проекта (Watch Window), в которых может отображаться основная
информация, интересующая программиста в процессе отладки. Это может быть
информация не только о текущем состоянии переменных в памяти системы, но и о
текущем состоянии регистров процессора. Следовательно, регистры ЦПУ могут быть
наблюдаемыми переменными.
Одним из преимуществ метода отладки программ с использованием окон
наблюдаемых переменныхWatch является возможность представления в них данных в
десятичной системе счисления, если она выбрана как система по умолчанию. Это очень
удобно, так как в окне регистров данные будут в Heх-формате, а в окнах Watch – в Decформате, то есть информация будет представлена в двух системах счисления
одновременно.
Открыть такое окно можно из меню View (Просмотр), выбрав команду Watch
Windows (Окна наблюдаемых переменных) или
щелкнув мышью на соответствующей пиктограмме
на
панели инструментов. Всего можно открыть два окна:
Watch1 и Watch2. В появившемся окне нужно
последовательно указать имена наблюдаемых переменных
или регистров с помощью ввода соответствующих
выражений. Из примера выше видно, что регистры r0 и r1 стали наблюдаемыми
переменными. В столбце Value будет отображено текущее значение наблюдаемых
переменных (в установленной системе счисления), а в столбце Type – тип переменных (в

217

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
данном случае ulong – длинное целое без знака). Значения в окне наблюдаемых
переменных будут автоматически обновляться в процессе отладки.

11.4 Арифметические операции с непосредственными
операндами
Операции с непосредственными операндами часто используются
для:
1) смещения содержимого регистров-указателей на 1, 2, 4, 8 байт для
доступа к очередному байту, полуслову, слову или двойному слову в
памяти;
2)
смещения содержимого регистра-указателя на величину индекса
при доступе к отдельным элементам массивов или структур данных;
3) инкрементирования/декрементирования счетчиков числа выполненных циклов.
Во всех перечисленных выше случаях величина смещения невелика – обычно не
выходит за пределы байта. При этом можно применять команды сложения/вычитания
содержимого регистра-приемника со вторым универсальным операндом Op2, заданным в
виде непосредственной константы. Диапазон возможных непосредственных констант
может быть расширен с 8 до 12 разрядов (до 0x0FFF) с использованием команд
сложения/вычитания с 12-разрядными непосредственными операндами #imm12:
ADD
ADDW
SUB
SUBW

{Rd},
{Rd},
{Rd},
{Rd},

Rn,
Rn,
Rn,
Rn,

#imm12
#imm12
#imm12
#imm12

Непосредственный 12-разрядный операнд – от 0 до 4095 (до 0x0FFF)
Первый операнд-источник
Регистр – приемник (опция)
W – Опция, указывающая на использование 12-разрядного непосредственного операнда
ADD – (ADD) - Сложить c 12-разрядным операндом: Rd ← Rn + #imm12
SUB – (SUB) - Вычесть 12-разрядный операнд:
Rd ← Rn - #imm12

Для
определенности
Вы
можете использовать суффикс «W»
в мнемонике команды. Однако, если
суффикс
«W»
отсутствует,
транслятор с Ассемблера все равно
сгенерирует правильную команду,
если введенная Вами константа на
самом деле является 12-разрядной.
Эти
команды
имеют
особенности: 1) не выставляют
флагов результатов операций; 2) не
допускают использование суффикса
«S» принудительной установки
флагов; 3) не могут быть выполнены
условно. Поэтому они применяются,
главным образом, для смещения
указателей на величину индекса при

218

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
доступе к массивам и различным структурам данных.
Пример использования команд сложения и вычитания с непосредственными
данными представлен в проекте CPU_8 (MyProg_8.s). После инициализации регистра r0
выполняется несколько команд увеличения и уменьшения его содержимого на константы,
не превышающие байта, в том числе на константы, предварительно обработанные в
сдвиговом регистре ЦПУ. Дальше демонстрируются команды сложения/вычитания с 12разрядными непосредственными операндами, расширяющие диапазон возможных
смещений. Последняя закомментированная команда показывает, что константы большей
разрядности (больше 12 бит), в том числе 32-разрядные константы, в качестве
непосредственных операндов в арифметических операциях недопустимы – транслятор
выдаст предупреждение об ошибке.
В конце программы показано,
как в арифметических операциях
можно использовать константы,
предварительно определенные с
помощью директивы EQU.
1) Откройте окно наблюдаемых переменных – регистров ЦПУ r0 и r1.
Установите в качестве системы счисления по умолчанию десятичную
систему.
2) Выполните программу в пошаговом режиме и наблюдайте одновременно
за содержимым регистров r0, r1 в регистровом окне (Hex-формат) и в окне
наблюдаемых переменных (Dec-формат). Убедитесь, что итоговый результат
полностью соответствует алгоритму.
3) Уберите символ комментария в строке 38 программы. Выполните трансляцию
программы. О чем говорит выданное транслятором сообщение об ошибке?
4) Просмотрите файл листинга и убедитесь в том, что команды ADD Rd, #imm12 и ADDW
Rd, #imm12 генерируют один и тот же код операции.
5) Почему в системе команд процессора недопустимы 32-разрядные непосредственные
операнды. Как же быть, если они реально требуются?
Используйте команду управления отладкой: Radix=10
Последнее содержимое окна Watch1
подтверждает правильность выполненных
операций.
3) Сообщение об ошибке информирует программиста о
выходе непосредственного операнда 0xFAFA за допустимый диапазон 12-разрядных
1)
2)

значений для данной команды. В нем транслятор напоминает о том, что возможные
значения не должны превышать 0x0FFF:
4) Это действительно так. Суффикс «W» можно опускать. Транслятор, будучи
интеллектуальным, сам сгенерирует нужный код, если обнаружит 12-разрядную
константу.
5) В этом случае в формате команды не останется ни одного бита для представления
кода операции. Выход есть – выполните инициализацию любого свободного регистра
ЦПУ 32-разрядной константой с помощью псевдокоманды LDR Rd, =Const.
Используйте этот регистр в нужной арифметической операции.

219

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
В арифметических операциях без каких-либо ограничений можно
использовать любые байтовые непосредственные константы. Использование
12-разрядных констант возможно, но без формирования флагов результатов
операции и условного выполнения команд. При разрядности константы,
превышающей 12 бит, потребуется ее предварительная загрузка в один из
регистров ЦПУ с использованием псевдокоманды загрузки, создающей в кодовой памяти
литеральный пул с численным значением этой константы.

11.5 Арифметические операции с массивами данных в
памяти
11.5.1 Обработка массивов 32-разрядных слов
В
большинстве
вычислительных
задач
с
использованием
целочисленной арифметики разрядности АЛУ процессоров ARM (32) более
чем достаточно, и контроль возможных переполнений при значительном
числе операций может не потребоваться. Рассмотрим в качестве примера
процедуру расчета суммы заданного
числа
32-разрядных
слов,
расположенных в ОЗУ (проект
CPU_9, программа MyProg_9.s). Она
предполагает сложение всех слов
массива,
начальный
адрес
расположения которых передается в
подпрограмму содержимым регистра
r0, а длина массива – содержимым
регистра r1. Подпрограмма выполняет
расчет суммы и сохраняет ее в
следующей после массива свободной
ячейке памяти (записывает в «хвост»
массива). При этом используются уже
известные нам операции загрузки
регистров из памяти с использованием
базовой адресации с пост-смещением
указателя.
В конце основной программы
объявляется секция не инициализируемых
данных, значения которых устанавливаются
пользователем в окне памяти в процессе
отладки программы.
Начальный адрес этой области памяти RAM_0 передается в подпрограмму перед ее
вызовом. Вы можете выполнить
отладку программы, предварительно
проинициализировав память любыми
начальными значениями.
Ниже представлены несколько
наборов исходных данных в виде
220

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
шести слов без знака и слов со знаком в дополнительном коде с результатами
суммирования, сохраненными в конце массива. Естественно, сначала Вы должны
установить нужный формат выдачи данных в окно памяти («десятичное представление –
слова без знака» или «десятичное представление – слова со знаком»),
проинициализировать память и только затем запустить программу на выполнение (можно
в режиме RUN с последующим «ручным» остановом).
a) Первый массив 32-разрядных слов без знака:

б) Второй массив 32-разрядных слов без знака:

в) Третий массив 32-разрядных слов со знаком в дополнительном коде:

1) Почему в команде сохранения суммы (строка 44) не указана величина
пост-смещения?
2) Может ли в первых двух наборах исходных данных при одной из
операций сложения возникнуть перенос С (переполнение при работе с
числами без знака)? Проверьте!
3) Возникнет ли флаг переноса C при обработке первых слов массива в наборе
переменных со знаком? Проверьте!
4) Возникнет ли при суммировании этого массива флаг знакового переполнения V?
1) Это не требуется, так как сохраняется только одно слово.
2) Нет – числа слишком маленькие.
3) Да. Но мы не должны обращать на него внимания, так как работаем с
числами со знаком. Для них контролируется флаг знакового переполнения
V.
4) Нет, так как ни при одной операции сложения мы не выходим за формат допустимых
32-разрядных чисел со знаком.
Сложение и вычитание 32-разрядных слов без знака и со знаком в
дополнительном коде выполняется одними и теми же командами
процессора. В первом случае переполнение контролируется флагом
переноса C, а во втором – флагом знакового переполнения V.

221

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ

11.5.2 Арифметические операции с массивами байт и полуслов
Мы уже отмечали, что интерфейс микропроцессорной системы с
внешними устройствами (дискретным и аналоговыми датчиками) может
иметь существенно меньшую разрядность, чем разрядность процессора. При
этом внешние данные могут храниться в памяти в виде последовательностей
байт или полуслов (со знаком или без). Процессоры ARM обеспечивают обработку таких
данных с авторасширением формата до размера 32-разрядного слова.
Приведем пример. Данные расположены в ОЗУ в виде последовательности
полуслов со знаком. Нужно найти контрольную сумму всех полуслов в этом массиве. В
рассмотренной выше подпрограмме необходимо сделать минимальные изменения: вместо
команды загрузки полного слова из
памяти
использовать
команду
загрузки
полуслова
со
знаком
(LDRSH) с авто-расширением до 32разрядного формата (см. MyProg_9_1.s).
Введем в память произвольную последовательность полуслов со знаком и
убедимся в том, что результат расчета контрольной суммы правильный. Обратите
внимание, как сумма массива -25 представлена в виде 32- разрядного слова: в младшем
полуслове «-25», а в старшем «-1», то есть только один знаковый разряд, расширенный на
все старшие разряды числа.

1)
Выполните отладку программы MyProg_9_1.s на нескольких наборах
исходных данных. Какое максимальное число полуслов может быть
обработано без знакового переполнения?
2)
Какие изменения необходимо сделать в этой подпрограмме для
поиска суммы полуслов без знака?
3) Как найти сумму массива байт со знаком?
4) Сумму массива байт без знака?
1) Без переполнения можно сложить до 65536 полуслов со знаком. При
этом сумма не выйдет за пределы 32-разрядной сетки процессора.
2) Достаточно заменить всего одну команду: вместо LDRSH
использовать команду LDRH (MyProg_9_2.s).
3) Используйте команду загрузки в регистр байта со знаком с авторасширением знака на все старшие разряды слова LDRSB (MyProg_9_3.s).
4) Используйте команду загрузки в регистр байта без знака LDRB с расширением влево
нулями.
Можно использовать попутные операции авторасширения формата
байта или полуслова до формата полного слова при загрузке данных в
регистры ЦПУ. При арифметической обработке таких данных вероятность
возникновения переполнения (знакового и беззнакового) минимальна.

222

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ

11.6 Многословная арифметика
Арифметика чисел большой разрядности используется, в частности,
при сложении/вычитании двойных слов – 64-битовых. Пусть имеются два
таких слова D1 и D2. Требуется найти их сумму и разность.
Каждое двойное слово состоит из младшего и старшего обычного
32-разрядного слова. В таблице слева показаны обозначения этих
компонент двойных слов. Операции
D1_High (r2)
D1_Low (r1)
сложения/вычитания
начинаются
с
D2_High (r4)
D2_Low (r3)
младших слов. При этом используются
Команды сложения двойных слов
ADC #0
ADCS
ADDS
обычные команды сложения/вычитания
Команды вычитания двойных слов
ADDS и SUBS обязательно с суффиксом
SBC #0
SBCS
SUBS
«S»
установки
флагов
результатов
Результат сложения/вычитания
операции
для
последующего
учета
SUM_H (r7)
SUM_W (r6)
SUM_L (r5)
возможного переноса или «заема» в
SUB_H (r7)
SUB_W (r6)
SUB_L (r5)
старшие разряды.
Сложение/вычитание
старших
слов выполняется с учетом ранее
возникшего переноса/«заема» и также с
учетом суффикса «S» установки флагов
сложения/вычитания
ADDS/SUBS.
Учет
возможного
переноса
за
пределы
64-битной
разрядной сетки можно выполнить
командой сложения нуля с флагом
переноса и вычитания из нуля флага
«заема».
Естественно, указанные выше
операции можно выполнить только с
операндами в регистрах процессора.
Пусть для размещения данных в ЦПУ
выбраны регистры, указанные в таблице
в
скобках.
Напишем
простую
программу, которая будет использовать
команды непосредственной загрузки данных в регистры процессора с вычислением
суммы и разности двойных слов MyProg_10.s, (проект CPU_10).
Отладку программы целесообразно выполнить по точкам останова в конце каждого
фрагмента: вычисления сначала суммы, а затем разности двойных слов. Содержимое окна
регистров,
показанное
ниже,
подтверждает
правильность алгоритма. Обратите внимание на то, что
при сложении двойных слов учтен возникший флаг
переноса и самое старшее слово суммы равно 0x1. При
вычитании «заема» нет, поэтому старшее слово разности – нулевое.
Команды сложения/вычитания с учетом переноса/«заема» позволяют
выполнять арифметические операции с числами неограниченной
разрядности, как со знаком, так и без знака.

223

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ

11.7 Макроопределение. Макровызов. Использование
макробиблиотек
11.7.1 Общие положения
Этот параграф при первом изучении можно просмотреть бегло. Он
рассказывает о том, что такое макросредства языка Ассемблер и как ими
пользоваться при программировании на Ассемблере. Дело в том, что при
огромных возможностях системы команд процессора, которые мы еще только
начинаем изучать, число регистров процессора ограничено. При разработке алгоритмов
решения вычислительных задач очень трудно держать в голове распределение регистров
процессора по функциональному признаку. Было бы неплохо разработать алгоритм
решения часто встречаемой задачи так, чтобы в нем использовались лишь символические
имена переменных, соответствующие их назначению (макроопределение – макрос). А
дальше, при вызове макроса, указать транслятору, какие фактические имена регистров
процессора или переменных следует использовать вместо символических имен.
Для решения подобных задач применяются специальные директивы языка
Ассемблер, которые позволяют описать алгоритм решения задачи с использованием
символических переменных, которые будем называть формальными параметрами.
Описание алгоритма с использованием формальных параметров называется
макроопределением – это фрагмент текста на языке Ассемблер, заключенный в
операторные скобки из двух директив Ассемблера MACRO (начало макроопределения) и
МEND (конец макроопределения). Синтаксис макроопределения:
MACRO
{$label} MacroName{$cond} {$parameter{,$parameter}...}
; Тело макроопределения (любая последовательность кода)
MEND
Между операторными скобками в первой строке указывается название
макроопределения (макроса) MacroName – символическое имя, по которому определенная
в макросе последовательность команд может быть вызвана в любом месте программы.
Это должно быть любое имя, кроме зарезервированных имен команд процессора или
директив ассемблера.
После имени макроса указывается список формальных параметров, которые
соответствуют физическому смыслу располагаемых в них данных. Их имена
предваряются знаком доллара «$» и отделяются друг от друга запятой. Число формальных
параметров не ограничивается. Именно формальные параметры могут использоваться в
любых командах процессора, расположенных в теле макроса, вместо имен регистров и
имен реальных переменных проекта.
Опционально в качестве параметров могут использоваться и метки начала макроса
$label, которые при макровызове сопровождаются генерацией оригинальных имен меток
с дополнительным цифровым кодом для исключения эффекта многократного определения
имен при многократных вызовах макросов.
Опционально в качестве формального параметра можно указать и код условного
выполнения создаваемой «новой» команды процессора $cond. При вызове он должен быть
заменен фактическим параметром – реальным кодом условного выполнения.
Макровызов представляет собой имя макроса со списком фактических параметров
(имен реальных регистров процессора, реальных переменных, констант). При
макрорасширении
вместо
формальных
параметров
транслятор
генерирует
соответствующие им фактические параметры, выполняя своеобразную автоматическую
224

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
замену переменных. Это позволяет автоматизировать процесс генерации кода и исключает
возможные ошибки. Достаточно отладить один раз макрокоманду и затем использовать ее
многократно.

11.7.2 Примеры макроопределений суммы и разности двойных слов
Используем введенные выше символические имена компонентов двойных слов –
источников данных (D1_H, D1_L), (D2_H, D2_L) и символические имена их суммы или
разности (R_H, R_W, R_L) для
разработки
макроопределений.
Расположим
макроопределения
в
самом начале программы пользователя
MyProg_10_1.s,
так
как
они
представляют собой лишь текстовые
описания
соответствующих
алгоритмов. Как видите, макрос
суммирования длинных слов получил
название D_ADD, а их вычитания –
D_SUB.
Всего
в
каждом
макроопределении по 7 формальных
параметров, имя каждого из которых
предваряется знаком доллара «$».
В теле макроопределения вместо имен регистров процессора использованы их
символические имена, смысл которых отражает расположенные в них данные. Так,
$D1_H, $D1_L – формальные параметры, содержащие старшее и младшее 32-разрядные
слова первого 64-битового двойного слова. При использовании таких имен смысл
выполняемых операций в теле макроса становится более понятным. Так, первая команда
ADDS выполняет сложение младших слов исходных двойных слов – $D1_L, $D2_L, с
сохранением результата в младшем слове результата $R_L. Макроопределение начинается
директивой MACRO и заканчивается директивой MEND.

11.7.3 Как вызываются макрокоманды?
После
того,
как
макроопределения сделаны, то есть
нужные алгоритмы описаны на языке
формальных параметров, они могут
быть использованы в любом месте
программы пользователя с помощью
макровызовов – символических имен новых макрокоманд со списком фактических
параметров, которые транслятор должен использовать вместо формальных параметров
при генерации кода макроса. Такая генерация кода называется макрорасширением.
Выполните
трансляцию
программы
MyProg_10_1.s.
Откройте
файл листинга. Вы увидите,
что
макроопределение
отображается в нем просто
как текст, не требующий
преобразования в машинный
код, а макровызов – автоматически преобразуется в нужный набор реальных команд
процессора, в котором каждый формальный параметр заменен фактическим (в данном
225

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
случае – именем используемого регистра процессора). Транслятор не только выполняет
эту замену переменных, но и создает машинный код соответствующих инструкций. Одна
строка исходной программы с макровызовом (строка 42) автоматически заменяется
нужным числом строк с реальными командами процессора.
Перед
вызовом
макросов
должны быть установлены исходные
значения двойных слов. В нашем
примере инициализация выполняется
путем
последовательной
загрузки
регистров (r2, r1) и (r4, r3) непосредственными словами-константами, после чего
выполняется вызов макросов расчета суммы и разности. Выполните отладку этой
программы и убедитесь в правильности полученных результатов.

11.7.4 Как использовать макробиблиотеки?
Все нужные программисту макроопределения можно записать в отдельный файл,
который будет называться библиотекой макроопределений, например, MyMacro.s. Если
функции, находящиеся в этом файле отлажены, то можно просто подключить к текущему
файлу приложения библиотечный файл с использованием директивы ассемблера GET
(«Получить файл») или INCLUDE («Подключить файл»):
GET filename или INCLUDE filename
При выполнении этой псевдокоманды указанный файл включается в программу
«как есть», без какой-либо обработки. Если в тексте программы-приложения встречается
макровызов, то он автоматически заменяется соответствующим расширением из
библиотеки. Это позволяет не только существенно экономить место в основной
программе, но и делать ее более наглядной и удобной для последующей модификации
(сопровождения). В имени подключаемого файла можно указывать полный путь доступа,
если файл не находится в текущем каталоге. По умолчанию текущим является каталог, в
котором находится файл программы приложения.
В подключаемых файлах могут
находиться не только макроопределения,
но и определения переменных проекта,
сделанные с помощью директивы EQU.
Имейте в виду, что эти директивы не
применяются для подключения объектных
библиотек.
Файл
с
библиотекой
макроопределений в конце должен иметь
директиву конца Ассемблерного текста
END.

226

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
Сохраним два наших макроопределения в файле MyMacro.s и
подключим этот файл к программе приложения MyProg_10_2.s, в котором в
начале файла находится директива GET подключения макробиблиотеки. О
том, что библиотека действительно подключена, можно удостовериться в
файле листинга. В нем же можно посмотреть, как макрокоманды
расширяются в последовательности реальных операций процессора. Выполните отладку
этой программы, убедитесь в правильности рассчитанной суммы и разности двойных
слов.
Макросредства – эффективный метод представления нужных
пользователю алгоритмов с использованием символических имен регистров и
переменных, что делает программу более понятной и «читабельной»,
сокращая ее объем. С их помощью можно даже создавать недостающие для
конкретных приложений команды процессора.
1) Что такое формальный параметр?
2) Что такое фактический параметр?
3) Чем макрос отличается от подпрограммы? Преимущества
недостатки макросов по сравнению с подпрограммами.

и

1) Символическое имя, которое используется в макро-описании вместо
имени регистра ЦПУ или адреса переменной в памяти, отражающее смысл
расположенных в нем данных.
2) Реальное имя регистра ЦПУ или имя переменной в памяти.
3) При каждом макровызове, в отличие от подпрограммы, в текст
программы вставляется фрагмент ассемблерного текста, выполняющий
нужную функцию. Если вызовов много, то объем программного кода увеличивается
по сравнению с подпрограммой. Однако, время выполнения макроса меньше, чем
время выполнения подпрограммы, так как не используются команды для вызова и
возврата из подпрограммы, а также команды сохранения контекста в стеке.
1) Разработать макроопределение функции D_LOAD, копирующей из
массива двойных слов в ОЗУ два двойных слова с записью их значений в
регистры процессора – формальные параметры (D1_H, D1_L), (D2_H,
D2_L). Двойные слова расположены в памяти с обычным порядком
расположения слов: младшее слово по младшему адресу, старшее – по
старшему. Начальный адрес массива находится в регистре-указателе процессора,
определенном формальным параметром A_D. Установить указатель адреса A_D на
следующий свободный адрес памяти.
2) Разработать макроопределение функции SAVE_3W, сохраняющей тройное слово
(из 3-х обычных слов) (R_H, R_W, R_L) в памяти, начиная с адреса,
расположенного в регистре-указателе A_D.
3) Включить обе функции в макробиблиотеку MyMacro_1.s и с ее помощью создать
подпрограмму сложения D_ADD двух двойных слов из массива в памяти,
начальный адрес которого передается содержимым регистра r0.
4) Написать основную программу с передачей параметров в подпрограмму и ее
вызовом. Отладить на тестовом наборе двойных слов в памяти.

227

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
1) Пример макроопределения содержится в файле MyMacro_1.s.
В качестве имени регистра-указателя используется формальный
параметр $A_D (адрес расположения двойных слов в ОЗУ). При загрузке
слов из памяти применяется адресация с пост-смещением указателя на 4
байта.

2) Пример второго макроопределения находится в том же файле. Для обозначения
регистра-указателя использован
тот же формальный параметр
$A_D.
Младшие
слова
сохраняются в режиме постсмещения указателя на 4 байта,
старшее слово – без смещения.
3) Фактически подпрограмма состоит из трех вызовов макросов. Первый вызов
обеспечивает загрузку двух
двойных слов в регистры ЦПУ,
второй выполняет расчет суммы
с
учетом
возможного
переполнения (из 3-х слов), а
третий
сохраняет результат
расчета в «хвосте» массива
исходных данных. В конце
подпрограммы следует возврат в
основную программу.
4) В самом начале основной программы (см. MyProg_10_3.s) нужно подключить файл с
новой макробиблиотекой, содержащей все
макроопределения MyMacro_1.s. Передача
параметров
в
подпрограмму
сводится к загрузке начального
адреса
размещения
массива
исходных данных RAM_0 в
регистр-указатель r0. Дальше следует вызов
подпрограммы. В конце основной программы
не забудьте открыть секцию данных и
зарезервировать в ней нужное число байт под переменные проекта. Выполните
трансляцию и сборку файлов проекта. После загрузки программы в симулятор
проинициализируйте память начальными значениями двух двойных слов. Помните,
что принятый в проекте порядок расположения слов в памяти такой – младшее слово
находится по младшему адресу.
228

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
Выполните программу. Один из наборов исходных данных и результаты
суммирования двойных слов представлены ниже (в окне памяти используется Hexпредставление чисел без знака).

Используйте макросы для формального описания достаточно
сложных алгоритмов обработки данных в ЦПУ с помощью формальных
параметров. Включайте макрокоманды в подпрограммы. Это позволит
объединить преимущества обеих технологий (макрокоманд и подпрограмм).
Используйте макросы, в том числе, для передачи параметров в
подпрограммы.
1) Имеется ли в процессорах ARM специальный флаг «заема» Borrow?
2) Нужно ли в обычных операциях сложения/вычитания байт/полуслов,
загруженных из памяти в регистры ЦПУ с авто-расширением нулями (для
чисел без знака) или знаковым разрядом (для чисел со знаком), учитывать
флаг переноса/«заема»?
3) Каков будет формат суммы массива из 100 полуслов без знака?
1) Нет. Вместо него применяется инверсное значение флага C.
2) При единичных операциях – нет. Результат всегда будет в пределах
разрядной сетки процессора. При обработке массивов – это зависит от
длины массива.
3) Максимальное значение суммы 65535*100 = 6553500, что
существенно меньше максимального положительного 32-разрядного числа (232-1) =
4 294 967 295. Контроль переполнения не требуется.

11.8 Команды умножения и деления
Процессоры ARM-Cortex M3/M4/M4F, в отличие от простых
микроконтроллеров, поддерживают операции умножения и деления 32разрядных операндов с получением 32-разрядного результата, а также
операции «длинного» умножения с получением 64-битного произведения
в двух регистрах ЦПУ.
Синтаксис операций первого типа (32) = (32) ∙ (32) следующий:
MUL {Rd}, Rn, Rm
UDIV {Rd}, Rn, Rm
SDIV {Rd}, Rn, Rm
Второй операнд-источник
Первый операнд-источник
Регистр – приемник (опция)
MUL - (Multiply) – Умножение 32-разрядных операндов:
Rd ← Rn ∙ Rm
DIV - (Divide) – Деление 32-разрядных операндов:
Rd ← Rn / Rm
U – (Unsigned) – Операция с числами без знака
S - (Signed) – Операция с числами со знаком в дополнительном коде

229

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
Команда MUL является универсальной. Она выполняет умножение 32-разрядных
операндов без знака или со знаком в регистрах источниках Rn и Rm. Она рассчитана,
главным образом, на умножение операндов, полученных путем предварительной загрузки
из памяти 16-разрядных полуслов без знака или со знаком (команды LDRH, LDRSH), а
также байт без знака или со знаком (LDRB, LDRSB), c автоматическим расширением
влево до формата 32-разрядного слова: (U16→U32, S16→S32) или (U8→U32), (S8→S32).
Как правило, периферия микроконтроллеров является 16-разрядной или даже 8разрядной. Поэтому исходные переменные в большинстве случаев имеют формат, не
превышающий 16-и разрядов. При умножении любых чисел (U16*U16) или (S16*S16)
результат никогда не превысит формат U32 или S32, то есть будет абсолютно точным в
пределах 32-разрядной сетки процессора. Исходя из этого, команда MUL не контролирует
переполнение при умножении. Предполагается, что программист использует входные
операнды в форматах, при которых переполнения невозможны.
На самом деле, как будет показано ниже, команда MUL генерирует правильный
результат и в том случае, когда исходные операнды являются 32-разрядными, но лишь
тогда, когда произведение попадает в допустимый диапазон представления 32-разрядных
чисел без знака или со знаком. В противном случае, старшее слово произведения теряется,
и результат умножения оказывается недостоверным.
Команды UDIV и SDIV имеют собственные префиксы «U» и «S», которые
специфицируют тип выполняемой операции: с числами без знака или со знаком. Целая
часть частного сохраняется в регистре назначения Rd, а остаток отбрасывается.
Обратите особое внимание на то, что контроль переполнения при операциях
умножения не предполагается. Программист должен обеспечить отсутствие возможных
переполнений правильным выбором форматов исходных данных. Поэтому, если
опасность переполнения имеется, то нужно использовать команды «длинного умножения»
с суффиксом «L» для получения 64-битного произведения сразу в двух регистрах
процессора (64) = (32) ∙ (32):
UMULL RdLo,RdHi,Rn,Rm
SMULL RdLo,RdHi,Rn,Rm
Второй операнд-источник (множитель)
Первый операнд-источник (множимое)
Регистр – приемник старшего слова произведения
Регистр – приемник младшего слова произведения
L (Long) – «Длинная» операция умножения с получением 64-битного результата
MUL - (Multiply) – Умножение 32-разрядных операндов:
(RdHi, RdLo) ← Rn ∙ Rm
U – (Unsigned) – Операция с числами без знака
S – (Signed) – Операция с числами со знаком в дополнительном коде

Команды «длинного» умножения полностью исключают возможность получения
некорректного результата. При этом разрядность АЛУ процессора фактически
увеличивается с 32 до 64 разрядов! С учетом рассмотренных выше операций сложения и
вычитания длинных слов появляется возможность выполнения любых арифметических
операций над 32-разрядными словами с получением 64-битного результата, при
отсутствии опасности переполнения.
В следующем параграфе будут рассмотрены операции множественной
загрузки/сохранения содержимого регистров ЦПУ (из памяти/в память), которые
значительно облегчают не только реализацию сложных вычислительных алгоритмов, но и
их отладку.

230

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ

11.9 Команды множественной загрузки/сохранения
регистров
11.9.1 Назначение и области применения
Процессоры ARM имеют ряд эффективных команд, которые
целесообразно использовать при реализации вычислительных алгоритмов
и при их отладке. В первую очередь это касается команд множественной
загрузки данных из памяти в регистры ЦПУ и множественного
сохранения содержимого регистров в памяти. Так как вычислительные
операции могут выполняться только над данными, расположенными в
регистрах процессора, и там же сохраняются результаты расчетов, то наличие команд,
позволяющих проинициализировать сразу группу регистров или сохранить результаты
расчетов из группы регистров в определенной области памяти, является существенным
преимуществом ARM-процессоров.
Эти преимущества особенно наглядно проявляются в процессе отладки –
результаты расчетов в окне памяти среды μVision можно представить в любом удобном
для программиста формате (знаковых чисел или чисел без знака, в шестнадцатеричной
или в десятичной системе счисления). Наглядная визуализация результатов расчетов
позволяет быстро протестировать работу сложных алгоритмов, найти возможные ошибки,
сокращает время разработки программного обеспечения.

11.9.2 Синтаксис основных команд множественной загрузки/сохранения
регистров
Синтаксис двух, наиболее часто используемых команд множественной загрузки
LDM и множественного сохранения регистров STM, следующий:
LDM
STM

Rn{!}, reglist
Rn{!}, reglist

reglist – Листинг регистров ЦПУ, участвующих в операции множественной загрузки
или сохранения, заключенный в фигурные скобки {Ri, Rj,…Rm}
! – Опция авто-инкрементирования содержимого базового регистра (+4) после
выполнения очередной пересылки данных (increment after)
Rn – Имя базового регистра ЦПУ, содержащего начальный адрес области памяти
M – (Multiple) – Множественная операция пересылки 32-разрядных слов
LD – (Load) - Загрузка нескольких регистров ЦПУ из памяти
ST – (Store) - Сохранение содержимого нескольких регистров в памяти

Особенности команд:
1) Поддерживается косвенная адресация памяти по содержимому базового регистра Rn
(одного из регистров ЦПУ). Перед выполнением команды он должен содержать
начальный адрес области памяти, к которой последует обращение.
2) Имя базового регистра Rn указывается сразу за мнемокодом команды на месте первого
операнда команды. В отличие от описанной ранее мнемоники команд однократной
пересылки данных в память, символ косвенной адресации в виде квадратных скобок
вокруг имени базового регистра «[ ]» не используется.
3) Список (листинг) регистров процессора, которые загружаются данными из памяти или
содержимое которых сохраняется в памяти, указывается на месте второго операнда
команды. Он обязательно заключается в фигурные скобки {Ri, Rj, …Rm}, которые в

231

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
данном случае являются не признаком опции операнда, а обязательным атрибутом
списка регистров.
4) После каждого доступа к памяти по чтению или по записи адрес доступа к памяти
(точнее, используемое смещение относительно базового адреса) автоматически
инкрементируется на 4 байта, указывая на очередную ячейку памяти в направлении
восходящих адресов: Mem[Rd]; Mem[Rd+4]; Mem[Rd+8], … Данные извлекаются из
памяти или записываются в нее последовательно пословно.
5) Если после имени базового регистра Rn указана опция «!» (восклицательный знак), то
это признак пост-автомодификации содержимого базового регистра после каждого
доступа к памяти. Это требуется, в частности, при последовательной загрузке разных
групп регистров из двух рядом расположенных областей памяти. При этом можно без
переинициализации базового регистра выполнить подряд две команды множественной
загрузки/сохранения. Без символа «!» пост-инкрементируется только величина
смещения относительно текущего содержимого базового регистра, а с символом «!» –
и содержимое самого базового регистра.
6) Допускается указывать в листинге имена регистров в любой последовательности,
однако они будут сохраняться в последовательности, начиная с имени самого
старшего регистра в направлении самого младшего. Так, если список регистров имеет
вид {r4, r1, r3, r6, r2}, то операция множественного сохранения начинается с регистра с
максимальным номером r6 и продолжается в направлении убывающих номеров r4, r3,
r2, r1.
7) Порядок множественной загрузки данных из памяти обратный: сначала загружаются
данные в регистр с наименьшим номером, а затем – в направлении возрастающих
номеров регистров. Этот порядок обеспечивает работоспособность вложенных
подпрограмм.
8) В листинге допускается указывать диапазон регистров, участвующих в операции,
например, {r1 – r5}. Это эквивалентно списку: {r1, r2, r3, r4, r5}.
9) Команды множественной загрузки/сохранения являются прерываемыми. Это означает,
что при поступлении запроса прерывания/исключения последовательность операций
пересылки данных прерывается и автоматически возобновляется после завершения
обслуживания прерывания/исключения. В аппаратной части процессоров ARM
имеются специальные средства, которые позволяют сохранить информацию о том, с
какого места нужно возобновить множественную пересылку данных.
10) Базовый регистр не может входить в листинг регистров за исключением указателя
стека SP. Эта возможность применяется при сохранении/восстановлении контекста
программы при обработке прерываний. Не рекомендуется включать в список
регистров также счетчик команд PC, во избежание «случайных» переходов.
Команды множественной загрузки/сохранения регистров имеют
особый, отличающийся от всех других команд синтаксис: 1) нет привычного
символа косвенной адресации «[ ]»; 2) опция «!» означает не пре- а постмодификацию базового регистра; 3) в качестве первого операнда всегда
указывается имя базового регистра, а второго – листинг регистров ЦПУ.
Будьте внимательны!

232

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ

11.9.3 Использование операций множественной загрузки/сохранения
регистров для отладки арифметических алгоритмов
11.9.3.1 Вычисления с 32-разрядными числами без знака
Рассмотрим пример организации вычислений с 32-разрядными
операндами без знака (проект CPU_11, файл MyProg_11.s). Требуется
вычислить выражение F=X+(Y∙Z)+(V/Q), в котором все исходные операнды
предварительно размещены в текущей кодовой секции в виде таблицы
констант. Для этого используем
директиву
ассемблера
DCD
размещения в памяти констант-слов.
Распределим регистры ЦПУ для
удобного решения задачи, например
так: r0=r1+(r2∙r3)+(r4/r5).
Следовательно,
в
начале
алгоритма
нужно
проинициализировать
группу
регистров {r1 - r5} исходными
значениями, что можно сделать с
использованием
команды
множественной загрузки регистров из
кодовой памяти по известному
начальному адресу A_ROM.
Дальше следует реализация собственно
вычислительного алгоритма, особенность которого
– использование команды умножения MUL
(работает и для знаковых и для беззнаковых чисел)
и команды деления слов без знака UDIV. Для
сложения компонент используем две команды
ADD (одну трехоперандную, вторую двухоперандную). Результат расчета сохраним в
регистре r0.
Для проверки алгоритма на любых наборах переменных поступим так:
зарезервируем в конце программы некоторую область ОЗУ для размещения в ней
содержимого всех, используемых при вычислениях, регистров процессора.
Цель – записать исходные данные и результат расчета в память для удобного
анализа в окне дампа памяти среды μVision, настроенном на нужный нам формат выдачи
информации (в данном случае Decimal, Unsigned, Int – слов без знака в десятичной
системе счисления).
Это позволит задавать любые наборы исходных данных и просматривать как
результат расчета, так и промежуточные результаты в удобном для наблюдения формате.
Для сохранения результатов в памяти воспользуемся командой множественного
сохранения содержимого сразу группы регистров.

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

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
что в памяти последовательно сохранено содержимое шести регистров ЦПУ: r0, r1, r2, r3,
r4, r5. Все промежуточные результаты и итог вычислений соответствуют ожиданиям.

2)

3)
4)
5)

1) Можно
ли
выполнить
загрузку регистров ЦПУ
традиционным способом с
использованием следующих
команд: напомним, что X, Y, Z, … –, это адреса размещения
соответствующих операндов в кодовой памяти.
Приведите пример значений операндов Y и Z, при которых результат произведения
(Y∙Z) будет некорректным (возникнет переполнение разрядной сетки). Проверьте в
отладчике.
Приведите пример значений исходных операндов, при которых частное в операции
деления будет рассчитано неточно.
Возможно ли переполнение разрядной сетки при выполнении операций сложения?
Как его можно зафиксировать?
Какой флаг будет об этом свидетельствовать?
1) Да, конечно. Так как данные расположены «близко», можно
использоватьотносительную адресацию операнда по текущему состоянию
PC и смещению.

2) Для этой команды исходные операнды являются 32-разрядными
числами без знака U32*U32. В общем случае результат должен быть U64, но он всегда
«усекается» до 32 разрядов, то есть старшее слово произведения, если оно есть,
просто отбрасывается. При этом возможное переполнение не контролируется. Только
в том случае, когда исходные числа были в формате U16 и перед операцией
умножения загружены в регистры ЦПУ с расширением формата до U32,
переполнения при умножении полностью исключаются. В противном случае, любая
операция умножения исходных операндов, результат которой превышает
максимально возможное 32-разрядное число без знака 232-1=4,294,967,295, будет
выполнена некорректно (старшее слово произведения будет потеряно). Примеры:
(65536*65536); (171798692*25). Убедитесь в этом.
3) При операции деления остаток всегда отбрасывается. Например, при операции
(7000/21)=333.333 дробная часть результата будет потеряна.
4) Да. Для контроля используйте команды сложения с дополнительным суффиксом
«S» установки флагов.
5) Флаг переноса «C» (так как мы работаем с числами без знака).

234

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
11.9.3.2 Вычисления с 32-разрядными числами со знаком в дополнительном коде
Напишем программу для решения той же вычислительной задачи, но с
использованием слов со знаком в дополнительном коде (MyProg_11_1.s).
Добавим префикс «S» к команде деления слов SDIV. Команду умножения
MUL оставим без изменений, так как она по умолчанию работает и со
знаковыми операндами. Заметьте
также, что операции сложения чисел
со знаком не отличаются от операций
сложения чисел без знака. В конце
кодовой секции проинициализируем
таблицу исходных слов со знаком с
помощью директивы Ассемблера
DCD.
Для множественной загрузки
регистров процессора данными из
кодовой памяти используем тот же
базовый регистр r6, содержащий
начальный адрес размещения данных.
Завершим вычислительный алгоритм
операцией сохранения результатов
расчета в оперативной памяти для
последующего анализа. Для этой цели
включим в программу секцию данных
и зарезервируем в ней место под
переменные проекта (аналогично
предыдущей программе).
Выполним трансляцию и сборку файлов проекта. При выполнении отладки не
забудем открыть окно дампа памяти и установить в нем режим отображения информации
(Dicimal, Signed, Int – знаковых 32-разрядных слов). Результат работы программы на
наборе переменных, указанных в тексте программы, показывает, что реализация
алгоритма правильная.

1) Почему в программе нельзя использовать команду инициализации
базового регистра r6 в виде: ADR r6, A_RAM?
2) Почему в режиме отладки программы пришлось изменить формат
выдачи данных в окно памяти?
3) Куда записывается остаток от деления при выполнении операции
SDIV?
4) При каких исходных числах возможно переполнение в процессе операции
знакового умножения?
5) Возможно ли знаковое переполнение при выполнении операций суммирования.
Какой флаг в этом случае будет установлен и при каких условиях?

235

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
1) Метка A_RAM является «дальней» 0x20000000, а в командах
относительной адресации по счетчику команд PC величина смещения
ограничена.
2) Так как в программе используются слова со знаком.
3) При делении целых чисел остаток от деления отбрасывается. В
данном случае (-7000)/(+21)= -3333333. В результате получаем частное -333 (см.
содержимое регистра r4 в дампе памяти).
4) При любых операндах, когда произведение S32*S32=S64 будет выходить за
допустимый диапазон представления 32-разрядных чисел со знаком (-231 ÷ +231-1),
то есть (-2,147,483,648 ÷ +2,147,483,647). При этом старшее слово результата
отбрасывается, и никакого флага переполнения не формируется. Примеры
некорректных операций: (-1,000,000,000)*(+3); (+2,000,000,000)*(+2). Используйте
команду MUL при работе с расширенными до формата S32 16-разрядными
исходными операндами S16. В этом случае произведение (S16*S16) = S32 никогда
не выйдет за пределы разрядной сетки процессора.
5) Да. Контроль переполнений возможен при анализе состояния флага V, но только в
том случае, если команды сложения имеют опцию формирования флагов «S».

11.10
Пример использования команд «длинного»
умножения
В заключение этой главы рассмотрим пример использования операций
«длинного умножения». В каталоге CPU_11 имеется файл MyProg_11_2.s,
содержащий подпрограмму попарного умножения 32-разрядных слов без
знака, расположенных в памяти, начиная с заданного адреса в регистре r0, с
сохранением 64-битного произведения в оперативной памяти, начиная с заданного адреса
в регистре r1.Число пар исходных слов задается содержимым регистра r2.
В подпрограмме используется
команда множественной загрузки
регистров процессора (r3, r4) из
таблицы констант в ПЗУ с поставтоинкрементированием указателя
(r0)
для
многократного
последовательного
считывания
нескольких пар исходных слов из
памяти в регистры ЦПУ.
Результат
умножения
очередной пары слов записывается в
регистры (r5, r6) и, с помощью
команды
множественного
сохранения их содержимого в
памяти, сохраняется в ОЗУ. При
этом также используется команда с
пост- авто-инкрементированием указателя (r1) для последовательного сохранения любого
числа произведений в памяти.

236

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
Основная программа выполняет передачу параметров в подпрограмму и
осуществляет ее вызов.

В конце кодовой секции размещается блок инициализации исходных данных – слов
без знака.

Для размещения результатов вычислений в оперативной памяти, как обычно,
объявляется секция данных, в которой резервируется требуемое число слов (в данном
случае, с запасом).

Выполните трансляцию и сборку файлов проекта. Откройте окно памяти и
настройте его на выдачу информации в виде 32-разрядных слов без знака в десятичной
системе счисления. Выполните программу. Результат показан ниже: первое произведение
(700000*20) = 14000000 представлено ненулевым младшим словом и нулевым вторым.

Второе произведение (1,713,396*32,146) = 55,078,827,816 – уже двумя ненулевыми
словами. В Heх-формате произведение выглядит так – 0xCD2F43728 (при этом старшее
слово 0xC = 12D, а младшее D2F43728 = 3,539,220,264D), что соответствует информации,
выданной в окно памяти. Третье произведение представлено ненулевым младшим словом
(4100*2) = 8200 и нулевым старшим.
1) Выполните отладку программы с другими наборами исходных данных.
2) Модифицируйте
подпрограмму
для
вычисления
«длинного
произведения» слов со знаком в дополнительном коде и сохранения
результатов в памяти.
3) Что нужно изменить в основной программе для отладки новой
подпрограммы? Выполните отладку.

237

ГЛАВА 11. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
2) Используйте вместо команды UMULL команду SMULL. Измените
имя подпрограммы на MUL_S32_S32 (см. MyProg_11_3.s).
3) Кроме нового имени подпрограммы измените также исходные данные
в ПЗУ: введите слова со знаком. Не забудьте при отладке сменить формат
выдачи информации в окно памяти на знаковый.
При умножении переменных всегда должны использоваться форматы
исходных операндов и соответствующие им команды умножения, которые
исключают выход произведения за допустимые диапазоны чисел. В
противном случае возможно получение неправильных результатов, в
частности, усечение результата до одного слова вместо двух. Найти такие
ошибки чрезвычайно сложно. В конце книги мы рассмотрим формат чисел с плавающий
точкой и операции с такими числами, при которых отмеченные здесь проблемы
полностью исключаются.

Список рекомендуемой литературы
1) Джозеф Ю. Ядро Cortex-M3 компании ARM. Полное руководство/ Ю. Джозеф; пер..с
англ. А.В. Евстафеева. – М.: Додэка-XXI, 2012. – 552 c. ил. (Мировая электроника).
2) ARM DDI 0403C_errata_v3, ARMv7-M Architecture Reference Manual, Arm Limited,
2010.
3) ARM DDI 0337H, Cortex-M3. Technical Reference Manual. Arm Limited, 2010.
4) ARM DUI 0068B. ARM Developer Suite. Assembler Guide. Arm Limited, 2001.
5) ARM DUI 0553A. Cortex-M4 Devices. Generic User Guide. Arm Limited, 2010.
6) ARM DDI 0439D. ARM Cortex-M4 Processor. Technical Reference Manual. Arm Limited,
2013.
7) ARM DDI 0479B. Cortex-M System Design Kit. Technical Reference Manual. Arm
Limited. – 2011.
8) Texas Instruments. SPMU 159a. Cortex-M3/M4F Instruction Set. Technical User’s Manual.
Texas Instruments Inc. – 2011.
9) Mahout V. Assembly language programming: ARM Cortex-M3. – Wiley-ISTE. – 2012.
10) Fisher M. ARM Cortex M4 Cookbook. – Packt Publishing Ltd. – 2016.
11) Hohl W., Hinds C. ARM Assembly language. Fundamentals and Techniques. Second
Edition. – N.-Y.: CRC Press.– 2015. – 453 p.

238

ГЛАВА 12. РАБОТА С БИТОВЫМИ ПЕРЕМЕННЫМИ

12 РАБОТА С БИТОВЫМИ
ПЕРЕМЕННЫМИ. РЕАЛИЗАЦИЯ
ЛОГИЧЕСКИХ КОНТРОЛЛЕРОВ
И ДИСКРЕТНЫХ АВТОМАТОВ
Оглавление
12.1. Логические побитовые операции .................................................................................... 240
12.2. Операции сдвига ............................................................................................................... 242
12.3. Программная реализация логических контроллеров .................................................... 244
12.3.1. Базовые принципы программной реализации логических контроллеров: ......... 244
12.3.2. Реализация логических контроллеров по таблице истинности ........................... 245
12.3.3. Непосредственное вычисление логических функций ........................................... 248
12.3.3.1. Имеется ли в процессорах Cortex-M3/M4 битовый сопроцессор? .............. 248
12.3.3.2. Как программно реализовать битовый сопроцессор в ARM Cortex-M? ..... 248
12.3.4. Пример программной реализации логического контроллера с использованием
макробиблиотеки битового сопроцессора .............................................................................. 251
12.4. Блоки условного выполнения команд ............................................................................ 253
12.5. Команды тестирования .................................................................................................... 255
12.6. Реализация логических контроллеров методом тестирования битовых переменных
..................................................................................................................................................... 256
12.7. Программная реализация дискретных управляющих автоматов ................................ 259
12.7.1. Дискретные управляющие автоматы ...................................................................... 259
12.7.2. Традиционные способы программной реализации дискретных управляющих
автоматов. Недостатки и проблемы ......................................................................................... 261
12.7.3. Как составить граф дискретного автомата? ........................................................... 262
12.7.4. Программная реализация дискретного автомата .................................................. 266
12.7.4.1. Понятие интерпретатора вершины графа ...................................................... 266
12.7.4.2. Технология выдачи управляющих воздействий ............................................ 267
12.7.4.3. Технология проверки условий перехода и смены номера состояния ......... 268
12.7.4.4. Вызов интерпретатора вершины по таблице начальных адресов
подпрограмм .............................................................................................................................. 270

239

ГЛАВА 12. РАБОТА С БИТОВЫМИ ПЕРЕМЕННЫМИ

12.1 Логические побитовые операции
Логическими называются команды, которые работают с битовыми
переменными. В системе команд имеется пять основных логических
команд:

AND{S}{cond}{Rd},Rn,Op2
ORR{S}{cond}{Rd},Rn,Op2
EOR{S}{cond}{Rd},Rn,Op2
BIC{S}{cond}{Rd},Rn,Op2
ORN{S}{cond}{Rd},Rn,Op2
Второй универсальный операнд: 1) #Const; 2) Rm; 3) Rm, Shift
Первый операнд-источник
Операнд-приемник (опция). Если не указан, то приемником является первый
операнд-источник.
Код условного выполнения команды
S – (Set) – Опция установки флагов результатов операции: N, Z, C
AND – Logical AND – побитовое «Логическое И»:
Rd ← (Rn AND Op2)
ORR – Logical OR – побитовое «Логическое ИЛИ»:
Rd ← (Rn OR Op2)
EOR – Exclusive OR – побитовое «Исключающее ИЛИ»:
Rd ← (Rn XOR Op2)
BIC – Bits Clear – побитовое «Логическое И НЕ»:
Rd ← (Rn AND NOT Op2)
ORN – OR NOT – побитовое «Логическое ИЛИ НЕ»:
Rd ← (Rn OR NOT Op2)

Логические операции выполняются одновременно над всеми 32-мя битами первого
операнда-источника Rn и второго универсального операнда Op2, который может быть
непосредственной константой, регистром, или регистром, содержимое которого
предварительно (попутно) сдвигается на заданное число разрядов в кольцевом сдвиговом
регистре процессора. Таким образом, при выполнении логической операции над двумя
одноименными битами операндов (имеющих одинаковые номера), параллельно
выполняются такие же логические операции над всеми остальными битами 32-разрядных
операндов.
Часто содержимое второго операнда рассматривается как битовая маска, с
помощью которой выполняется модификация отдельных бит или целых битовых полей
первого регистра-источника. Ниже (табл. 12.1) показан принцип такой модификации на
примере одного байтового поля исходных операндов.
Таблица 12.1 Иллюстрация применения «битовых масок»
Логическая
операция

Назначение

И

Сброс в «0» бит, для которых в
маске установлены нули.

Пример
Операция
AND

ИЛИ

Установка в «1» бит, для которых
в маске установлены единицы.

Операция
OR

Исключающее
ИЛИ

Инвертирование бит, для которых
в маске установлены единицы.

Операция
EOR

Операнды/Результат
1 0 1 1 0 1 1
1 1 0 0 0 0 1

1
1

1 0 0 0 0 0 1
Операнды/Результат
1 0 1 1 0 1 1
0 0 1 1 1 1 0

1

1 0 1 1 1 1 1
Операнды/Результат
1 0 1 1 0 1 1
0 0 1 1 1 1 0

1

1

1

0

0

0

1

0

1

1
0

1
0

240

ГЛАВА 12. РАБОТА С БИТОВЫМИ ПЕРЕМЕННЫМИ
И НЕ

Сброс в «0» бит, для которых в
маске установлены единицы

Операция
AND NOT

ИЛИ НЕ

Установка в «1» бит, для которых
в маске установлены нули.

Операция
OR NOT

Операнды/Результат
1 0 1 1 0 1 1
1 1 0 0 0 0 1

1
1

0 0 1 1 0 1 0
Операнды/Результат
1 0 1 1 0 1 1
0 0 1 1 1 1 0

0

1

1

1

1

1

0

1

1

1
0

Вы наверняка заметили, что в представленном выше списке логических команд не
хватает операции «Логического НЕ». Эту функцию выполняет уже рассмотренная нами
раньше (см. 10.2) команда пересылки операнда с предварительной побитовой инверсией:
MVN{S}{cond} Rd, Op2
Она, в отличие от предыдущих команд, всегда двухоперандная. Регистр-приемник
указывается обязательно (не может быть опцией) и всегда принимает результат
побитового инвертирования.
Обратимся к проекту CPU_13, в котором (программа MyProg_13)
демонстрируются возможности команд маскирования битовых полей. Регистр
r0 загружается начальными данными, которые модифицируются с
использованием маски, загруженной в регистр r1. Первая команда AND r0, r1
использует маску «как есть», а вторая – выполняет «попутный» циклический сдвиг маски
на 4 разряда вправо (ROR #4). В результате нулевое поле маски перемещается в область
самых старших разрядов для очистки старшего нибла регистра r0. В программе показаны
возможности ввода непосредственных операндов в разных системах счисления, в том
числе в двоичной. Обратите внимание на то, что операция побитового инвертирования
выполняется командой MVN, а также на то, что для установки в «1» или сброса в «0»
нужного бита или битового поля можно использовать разные команды (ORR, ORN) и
(AND, BIC). Предпочтительнее та из них, для которой маска может быть задана более
коротким непосредственным операндом.

241

ГЛАВА 12. РАБОТА С БИТОВЫМИ ПЕРЕМЕННЫМИ
1) Какие операции сдвига называются «попутными»?
2) В чем их преимущество?
3) Выполните отладку программы. Убедитесь в правильности
операций маскирования данных.
4) Просмотрите исполняемый файл в окне Дизассемблера и найдите
команду, которую транслятор заменил на более компактную.
1) Операции со вторым универсальным операндом-источником, в
котором задается дополнительный сдвиг указанного типа содержимого
операнда на заданное число разрядов.
2) Фактически одной командой, за то же время (один такт)
выполняются две операции: сдвига и логическая или арифметическая.
3) Выполните программу в пошаговом режиме, проследите за содержимым регистра
r0: 0xFFAABB55; 0xFFAABB50; 0x0FAABB50; 0x0FAABB5F; 0xFFAABB5F;
0x005544A0; 0x005544A8; 0x005544A0; 0x005544A8; 0x00554428.
4) Это команда загрузки регистра r1: удалось заменить ее командой загрузки другой
константы с предварительной
побитовой инверсией. В
результате не пришлось создавать в кодовой секции литеральный пул с исходной
константой. Этот пример – демонстрация возможностей интеллектуального
транслятора с языка Ассемблер.

12.2 Операции сдвига
Мы рассмотрели ранее (см. 7.11.4) общий принцип выполнения
«попутных» операций сдвига данных в кольцевом сдвиговом регистре
процессора, если второй операнд-источник задается как «гибкий второй
операнд». Попутные операции сдвига можно использовать во многих
командах. Фактически одна такая команда выполняет сразу две операции:
предварительного сдвига содержимого второго операнда-источника и
основную операцию (арифметическую, логическую, пересылки) над результатом сдвига и
содержимым первого операнда-источника. Тем не менее, в состав команд процессора
включены отдельные команды сдвига, которые могут выполнять исключительно
операцию сдвига, рассматривая ее как основную, а не дополнительную:
ASR{S}{cond} Rd, Rm, Rs|#n
LSL{S}{cond} Rd, Rm, Rs|#n
LSR{S}{cond} Rd, Rm, Rs|#n
ROR{S}{cond} Rd, Rm, Rs|#n
RRX{S}{cond} Rd, Rm
Регистр Rs или константа #n – задает число разрядов сдвига
Операнд-источник – его содержимое сдвигается
Операнд-приемник. Не может быть опцией, указывается всегда
Код условного выполнения команды
S – (Set) – Опция установки флагов результатов операции: N, Z, C
ASR – Arithmetic Shift Right – Арифметический сдвиг вправо
LSL – Logical Shift Left – Логический сдвиг влево
LSR – Logical Shift Right- Логический сдвиг вправо
ROR – Rotate Right – Циклический сдвиг вправо
RRX – Rotate Right with Extend – Расширенный циклический сдвиг вправо на 1 разряд

242

ГЛАВА 12. РАБОТА С БИТОВЫМИ ПЕРЕМЕННЫМИ
Особенность команд в том, что тип операции сдвига определяется мнемокодом
команды, а число разрядов сдвига – содержимым регистра сдвига Rs или
непосредственной константой #n. Число разрядов сдвига можно задавать в диапазоне от 0
до 31. Исключение составляет операция арифметического сдвига вправо, для которой
величина сдвига находится в диапазоне 0÷32. При n = 32 во все разряды регистраприемника будет записан знаковый разряд операнда-источника. Для команд сдвига
указание имени регистра-приемника обязательно.
Опция установки флагов «S» позволяет модифицировать флаги N, Z и С по
результатам операции сдвига.
При нулевом числе разрядов сдвига предпочтительнее использовать вместо
операции сдвига ее аналог – команду пересылки (см. табл. 12.2).
Таблица 12.2 Пример эквивалентных команд
Команда сдвига

Эквивалентная команда пересылки

LSL{S}{cond} Rd, Rm, #0

MOV{S}{cond} Rd, Rm

Напротив, вместо команды пересылки MOV со вторым гибким операндом, если
число разрядов попутного сдвига отлично от нуля, целесообразно использовать ее аналог
– команду сдвига (см. табл. 12.3).
Таблица 12.3 Пример эквивалентных команд
Команда пересылки

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

MOV{S}{cond} Rd, Rm, ASR #n
MOV{S}{cond} Rd, Rm, LSL #n (if n != 0)
MOV{S}{cond} Rd, Rm, LSR #n
MOV{S}{cond} Rd, Rm, ROR #n
MOV{S}{cond} Rd, Rm, RRX

ASR{S}{cond} Rd, Rm, #n
LSL{S}{cond} Rd, Rm, #n
LSR{S}{cond} Rd, Rm, #n
ROR{S}{cond} Rd, Rm, #n
RRX{S}{cond} Rd, Rm

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

243

ГЛАВА 12. РАБОТА С БИТОВЫМИ ПЕРЕМЕННЫМИ

12.3 Программная реализация логических контроллеров
Логическими контроллерами называются устройства, которые,
получая с портов ввода микропроцессорной системы вектор текущего
состояния объекта управления (состояния дискретных датчиков и
исполнительных устройств объекта), называемый вектором входа X (xn,
xn-1,…x0), вырабатывают набор управляющих воздействий вектор выхода
Y (ym, ym-1,….y0), в строгом соответствии с системой логических (Булевых)
функций, описывающих поведение устройства:
𝑦𝑚 = 𝑓(𝑥𝑛 , 𝑥𝑛−1 , … 𝑥0 );

{
𝑦0 = 𝑓 (𝑥𝑛 , 𝑥𝑛−1 , … 𝑥0 );

(12.1)

Все входные переменные и выходные управляющие воздействия в таких
устройствах являются битовыми переменными, которые могут принимать только два
возможных значения «1» или «0». Входные переменные отражают состояние дискретных
датчиков, установленных на объекте (давления, расхода, температуры, конечного
положения исполнительных органов и т.д.), а выходные переменные являются командами
на включение/выключение соответствующих исполнительных устройств (контакторов,
реле, силовых ключей и т.д.).
В общем случае размерность вектора входа X может достигать нескольких сотен
компонент, а вектора выхода Y – нескольких десятков или сотен. Логический контроллер
может быть реализован аппаратно с помощью логических элементов малой степени
интеграции (элементы И, ИЛИ, НЕ и др.), средней степени интеграции (мультиплексоры,
шифраторы, триггеры, счетчики…) или большой степени интеграции (постоянная память
ПЗУ, программируемые логические интегральные схемы (ПЛИС), микроконтроллеры…).
Все решения предполагают реализацию системы логических функций (1). Рассмотрим
способы программной реализации системы Булевых функций на базе микроконтроллеров
с процессорами ARM Cortex-M3/M4.

12.3.1 Базовые принципы программной реализации логических контроллеров
1. Все порты ввода/вывода и регистры периферийных устройств в ARMмикроконтроллерах отображены на память, следовательно, получение входной
информации (вектора входа X) – это обращение к соответствующим ячейкам памяти
по чтению, а выдача управляющих воздействий (вектора выхода Y) – обращение к
соответствующим ячейкам памяти по записи. Число таких ячеек памяти определяется
разрядностью векторов входа и выхода и разрядностью портов ввода/вывода,
обеспечивающих сопряжение микроконтроллера с внешними устройствами.
2. В общем случае (см. главу 10) обращение к портам ввода/вывода и регистрам
периферийных устройств возможно по байтам (LDRB/STRB), полусловам
(LDRH/STRH) или словам (LDR/STR). Однако, в любом случае данные при чтении
попадают в 32-разрядные регистры ЦПУ, а при записи – извлекаются из них. Таким
образом, компоненты вектора входа X упаковываются в несколько 32-разрядных слов
в регистрах ЦПУ при опросе текущего состояния внешних входов, а компоненты
вектора выхода Y рассчитываются и сохраняются в нескольких регистрах ЦПУ перед
выдачей управляющих воздействий во вне.
3. Цикл работы логического контроллера (один проход, называемый сканом) состоит из
трех блоков:
244

ГЛАВА 12. РАБОТА С БИТОВЫМИ ПЕРЕМЕННЫМИ

4.

5.

6.

7.

8.

1) Получение извне всех компонент вектора входа X и размещение их в регистрах
ЦПУ;
2) Вычисление всех компонент вектора выхода Y для текущего набора входных
переменных X;
3) Выдача вектора выхода Y вовне – вывод управляющих воздействий.
Этот цикл должен повторяться бесконечно, в результате чего каждый раз
рассчитывается новый вектор выхода на основе текущего значения вектора входа.
Считывание абсолютно всех компонент вектора входа перед началом расчета вектора
выхода является обязательным. В противном случае, логический контроллер может
выдать не предусмотренный алгоритмом вектор управляющих воздействий с
непредсказуемыми результатами управления объектом.
Копия вектора входа X в регистрах ЦПУ является «образом входных битовых
переменных», а рассчитанный вектор выхода Y до его выдачи вовне – «образом
вектора выхода Y».
В большинстве практических случаев общего числа регистров ЦПУ для размещения в
них входных и выходных битовых переменных вполне достаточно. Так, если число
битовых входов не превышает 288, а число битовых выходов 64, возможно такое
распределение регистров ЦПУ:
1) (r1-r9) – компоненты вектора входа – до (9*32) = 288 бит;
2) (r11-r12) – компоненты вектора выхода – до (2*32) = 64 бит.
Если регистров процессора все же недостаточно, для размещения компонент векторов
входа и выхода используется память данных (ОЗУ). При этом ограничения по
размерности векторов снимаются. Однако, лучше проанализировать задачу и разбить
сложный логический контроллер на подконтроллеры, для каждого из которых число
входов и выходов будет ограничено. Возможно, это будет группа контроллеров,
работающих совместно, в том числе друг на друга.
Реализация логических контроллеров и дискретных автоматов с прямым доступом к
так называемым «битовым лентам в памяти» ARM-процессоров будет рассмотрена в
следующей главе.

12.3.2 Реализация логических контроллеров по таблице истинности
Если размерности векторов входа и выхода невелики, то
программист может рассчитать и записать в кодовую память
микропроцессорной системы управления таблицу истинности для всей
системы логических функций (1). Проиллюстрируем сказанное на примере
логического контроллера, обрабатывающего байтовый вектор входа (8
дискретных входов X0-X7) и генерирующего байтовый вектор выхода (8
битовых управляющих воздействий Y0-Y7). Таблица истинности содержит значения всех
логических функций на всех возможных наборах входных переменных. Число таких
наборов, т.е. строк в таблице истинности (пример см. в табл. 12.4), определяется
размерностью вектора входа, в данном случае 256:

245

ГЛАВА 12. РАБОТА С БИТОВЫМИ ПЕРЕМЕННЫМИ
Таблица 12.4 Таблица истинности
Вектор входа X
X7 X6 X5 X4

X3

X2

X1

X0

Вектор выхода Y
Y7 Y6 Y5 Y4

Y3

Y2

Y1

Y0

0
0
0

1
1
1

0
0
0

1
1
1

0
0
0

1
1
1

0
0
1

0
1
1

0
1
0

1
0
1

1
0
1

0
1
0

0
1
1

1
1
0

0
1
0

1
1
1

1
1
0

0
1
1

0
1
0

0
0
1

0
0
0

1
1
1

0
0
0

1
1
1

0
0
0

1
1
1

1
0
0

0
0
0

0
0
1

0
1
0

0
0
1

0
0
0

Таким образом, если вектор выхода имеет размерность 8, то таблица истинности
занимает всего 256 байт в кодовой памяти. При увеличении размерности вектора выхода
вдвое (16 бит) таблица истинности увеличивается до 256 полуслов, еще вдвое (до 32 бит)
– до 256 полных 32-разрядных слов.
В чем состоит идея метода? Если таблица истинности рассчитана и размещена в
памяти, то все что требуется – это считать из нее вектор выхода Y, зная значение вектора
входа X. Если вектор выхода байтовый, то значение X точно соответствует индексу в
таблице истинности (номеру строки) i=X. Если в таблице хранятся полуслова, то значение
индекса должно быть удвоено i=(2∙X), а если полные слова, то увеличено в 4 раза i=(4∙X).
Доступ к массивам данных по базовому адресу и индексу обеспечивается с
использованием базово-индексной адресации [Rn, Rm] с пре-индексированием. В этом
случае перед доступом к данным содержимое базового регистра Rn автоматически
увеличивается на значение в индексном регистре Rm. Используя такую адресацию можно
вообще без вычислений извлечь значение вектора выхода из таблицы истинности и
выдать его для управления объектом. Быстро и эффективно.
Пример программной реализации логического контроллера по таблице
истинности представлен в файле MyProg_13_2.s (проект CPU_13). Первое, что
должен сделать программист, это заполнить таблицу истинности данными в
соответствии с подлежащей реализации системой булевых функций. В нашем
случае каждый вектор выхода
представляет байт данных и для его
размещения в кодовой памяти служит
директива Ассемблера DCB. Таблица
истинности размещается в самом
конце кодовой секции и для ее
заполнения
можно
использовать
константы в любой удобной системе
счисления (например, в двоичной).
Необходимо определиться с
портами ввода входных битовых
переменных и вывода управляющих
воздействий.
Если
их
адреса
известны, то они должны быть
предварительно описаны, например, с помощью директивы EQU (Эквивалентно). Так как
мы будем отлаживать программу в симуляторе, объявим в ее конце секцию данных и
зарезервируем в ней всего два байта: X – для симуляции байтового порта ввода и Y – для
симуляции байтового порта вывода.

246

ГЛАВА 12. РАБОТА С БИТОВЫМИ ПЕРЕМЕННЫМИ
Теперь можно приступить к
написанию программы логического
контроллера. Для доступа к таблице
истинности
будем
использовать
регистр-указатель r0, для доступа к
порту ввода – регистр-указатель r1, к
порту вывода – регистр-указатель r2.
Так как содержимое базовых регистров
не будет меняться, в теле основного
вычислительного алгоритма их инициализация выполняется только один раз.
Собственно,
программа
логического контроллера предельно
проста: получение вектора входа из
порта ввода с сохранением в регистре
r3; извлечение вектора выхода в
регистр r4 из таблицы истинности по
базовому адресу в r0 и индексу в
регистре r3; выдача вектора выхода из
регистра r4 в порт вывода (по адресу в указателе r2). Дальше программа принудительно
зацикливается для получения очередного набора входных данных и расчета выходных
управляющих воздействий.
1) Выполните трансляцию и сборку проекта. Убедитесь в том, что
при задании в окне памяти любого (в пределах 0 – 10) вектора входа из
таблицы действительно извлекается нужное значение вектора выхода и
сохраняется в соответствующей ячейке памяти.
2) В чем на Ваш взгляд преимущества этого метода решения?
3) В чем недостатки?
4) Почему используемый в программе метод адресации данных в таблице истинности
называется базовым с пре-индексированием?
5) Что нужно изменить в программе, чтобы выполнялся доступ к 16-разрядным
векторам выхода?
2) Компактность кода, предельно высокая скорость выполнения
программы, максимально быстрая реакция на изменение входных
переменных.
3) Составление таблицы истинности – неблагодарный труд. Более
того, при любом изменении алгоритма ее коррекция требует времени. При
числе входных переменных, превышающих уже несколько единиц, таблица
получается объемной.
4) Индекс сначала добавляется к базовому адресу, и по полученному эффективному
адресу уже извлекаются данные.
5) Первое: заменить суффикс «B» (байт) на суффикс «H» (полуслово) в командах
основного тела алгоритма. Второе: после получения вектора входа из порта
удвоить его значение.

247

ГЛАВА 12. РАБОТА С БИТОВЫМИ ПЕРЕМЕННЫМИ

12.3.3 Непосредственное вычисление логических функций с использованием
логических команд
12.3.3.1 Имеется ли в процессорах Cortex-M3/M4 битовый сопроцессор?
В процессорах, ориентированных на эффективную реализацию
логических контроллеров и дискретных автоматов (например, для
использования в промышленных программируемых контроллерах,
решающих задачи комплексной автоматизации производства), обычно
применяются так называемые битовые сопроцессоры. Это устройства,
которые могут оперировать с отдельными битами, не меняя состояния
остальных бит. Они имеют битовый аккумулятор и допускают последовательное
вычисление любых логических выражений с получением результата вычисления в
битовом аккумуляторе (пример – микроконтроллеры Intel8051). Естественно, что
настоящий битовый сопроцессор поддерживает:
1) Загрузку нужного бита в битовый аккумулятор;
2) Логические операции с текущим содержимым битового аккумулятора и любыми
другими битами (И, ИЛИ, НЕ, ИЛИ НЕ, …);
3) Сохранение результата вычисления логического выражения в любом нужном
бите (сохранение содержимого битового аккумулятора).
В таких процессорах определенная область встроенной памяти отводится для
побитово-побайтово адресуемого ОЗУ, в котором поддерживается и байтовая адресация
данных и битовая адресация. Это означает, что каждый отдельный бит имеет свой
персональный адрес. Именно по этому адресу выполняется доступ к нужному биту в
отмеченных выше операциях с битами. В процессорах Cortex-M3/M4 в явном виде
битового сопроцессора нет, но он может быть «создан» самим программистом.
12.3.3.2 Как программно реализовать битовый сопроцессор в ARM Cortex-M3/M4?
Система команд, изучаемых нами процессоров, настолько мощная, что позволяет,
при необходимости, создать битовый сопроцессор с использованием макросредств
Ассемблера, то есть создать библиотеку макрокоманд, выполняющих функции битового
сопроцессора, причем не менее эффективно чем настоящий, аппаратно реализованный
битовый сопроцессор. Покажем, как это делается.
12.3.3.3 Функция битового аккумулятора
Эта функция может выполняться любым битом любого из регистров ЦПУ. Однако,
наиболее удобно для этой цели использовать самый старший бит регистра. Договоримся
называть этот регистр аккумулятором (символическое имя регистра-аккумулятора или
формальный параметр, используемый для его обозначения в макро-определениях – $A).
При вызовах макрокоманд оно будет заменяться реальным именем регистрааккумулятора, например, r0. Все логические операции будут выполняться параллельно
над всеми 32-мя битами регистров ЦПУ, но нас будет интересовать результат только в
старшем 31-м бите – остальные биты, независимо от их содержимого, будут
игнорироваться. Состояние 31-го бита легко проверяется по условиям: MI (N=1) –
результат отрицательный (Bit_31=1) и PL (N=0) – результат положительный (Bit_31=0).
Следовательно, после вычисления логического выражения можно будет сохранить
полученный битовый результат в нужном месте командами условного выполнения по
условию MI, или по условию PL.
12.3.3.4 Функция загрузки бита в битовый аккумулятор
Для ее реализации можно применить одну, из имеющихся в системе команд
процессора, операций сдвига, например, операцию логического сдвига влево LSL
248

ГЛАВА 12. РАБОТА С БИТОВЫМИ ПЕРЕМЕННЫМИ
содержимого регистра-источника на нужное число бит с расширением нулями справа. Мы
договорились, что образ вектора входа, то есть текущий набор входных битовых
переменных, создается в регистрах общего назначения процессора. Пусть один из таких
регистров имеет символическое имя $R, а конкретный бит, который должен быть
загружен из него в битовый аккумулятор – номер $N (обычный литерал, например, 5).
Число разрядов сдвига влево
определяется выражением #(31-$N) и
рассчитывается автоматически на стадии
ассемблирования.
В
соответствии
с
синтаксисом
команды
LSL
символ
непосредственного операнда «#» перед выражением обязателен.
Создадим библиотеку команд битового сопроцессора в файле Macro_bit.lib (проект
CPU_14).
При вызове макрокоманды формальный параметр $R будем заменять именем
конкретного регистра ЦПУ (например, r1), а формальный параметр $N – номером бита,
подлежащего загрузке (от 0 до 31). Вызов такой макрокоманды (по имени L_Bit)
предельно прост L_Bit r0, r1,5: загрузить в 31-й бит регистра-аккумулятора r0 пятый бит
регистра r1. В чем преимущество такой макрокоманды? Вы никогда не ошибетесь с
расчетом величины сдвига: эту операцию будет выполнять сам транслятор на стадии
ассемблирования программы.
12.3.3.5 Логические операции битового аккумулятора с битовыми операндами.
Макробиблиотека битового сопроцессора
В начале главы мы познакомились с
логическими
командами,
которые
выполняют сразу 32 параллельные операции
над
содержимым
первого
регистраисточника и второго-регистра источника,
который может быть так называемым вторым
универсальным операндом – регистром,
содержимое
которого
предварительно
сдвигается на нужное число разрядов с
использованием одной из имеющихся
операций сдвига. Это дает нам возможность
создать макрокоманды, в которых первым
регистром-источником
и
одновременно
регистром-приемником
будет
регистраккумулятор $A, а вторым регистром –
любой регистр ЦПУ $R, содержимое
которого сдвигается логически влево LSL (в
качестве примера) на число разрядов,
определяемое номером нужного бита $N.
Это позволит выполнить прямую логическую операцию 31-го бита регистраприемника (битового аккумулятора) и любого заданного бита регистра-источника.
Создадим такие макрокоманды для всех логических операций, поддерживаемых
процессором: И, ИЛИ, И НЕ, ИЛИ НЕ, Исключающее ИЛИ. Напомним, что синтаксис
второго универсального операнда Rm, Shift предполагает задание типа сдвига и через
пробел – числа бит сдвига в виде непосредственного операнда, в нашем случае – LSL #n.
Обратите внимание на использование в макрокоманде «Логическое И НЕ»
команды процессора BIC – «Очистить бит». Она сначала инвертирует нужный нам бит, а
затем выполняет операцию «И» с содержимым аккумулятора. Такие же особенности
249

ГЛАВА 12. РАБОТА С БИТОВЫМИ ПЕРЕМЕННЫМИ
имеет и макрокоманда «Логическое ИЛИ НЕ». Команда процессора ORN вначале
инвертирует нужный бит, а затем выполняет операцию логического «ИЛИ» с
содержимым аккумулятора. Обратите внимание и на то, что все логические операции
имеют суффикс «S» установки флагов. Флаги потребуются при сохранении результата
расчета.
Добавим к этим макрокомандам еще одну – инвертирования содержимого битового
аккумулятора. Получим функционально
полную (даже с избытком) группу
логических операций, с помощью которых
можно легко реализовать любую, сколь
угодно сложную, логическую функцию.
Напомним, что в общем случае для реализации любой логической функции достаточно
всего трех логических операций: И, ИЛИ, НЕ.
Осталось разработать макроопределение записи содержимого битового
аккумулятора в нужный бит одного из регистров, предназначенного для сохранения
компонентов вектора выхода. Обратим внимание на то, что все наши макрокоманды
выставляют флаги. Это не увеличивает время выполнения команд (все они выполняются
за один такт), но позволяет сразу после завершения некоторой последовательности
вычислений организовать эффективную пересылку содержимого битового аккумулятора
$A в нужный бит регистра назначения $R. Номер этого бита будем задавать формальным
параметром $N. Для установки бита по его номеру можно использовать логическую
операцию ИЛИ с маской, в которой «1» записана в разряд, соответствующий номеру бита,
а остальные разряды – нулевые. Получить такую маску можно автоматически, заставив
транслятор вычислить выражение (1