Россия, Новосибирск |
Программирование для встроенных систем
3.2. Разработка программ с помощью кросс-инструментария
Целью использования кросс-инструментов является создание на инструментальной машине файла с двоичным образом (firmware) начального содержимого памяти (как машинные команды, так и данные) для целевой аппаратной системы. Такой файл затем используется для загрузки в конкретные целевые устройства.
В качестве языков программирования встраиваемых систем выступают обычно C (а также его расширенные подмножества типа Embedded C) и ассемблер. Ассемблер используется как в виде вставок в код на языке С, так и в виде отдельных ассемблерных модулей. В случае встраиваемых систем программирование на ассемблере остается важной составляющей создания программ ввиду, как правило, жестких требований к высокой производительности и малому объему программного кода.
На рис представлена типовая схема разработки программ с помощью кросс-инструментария.
Обычно процесс взаимодействия программиста с кросс-инструментами происходит через визуальную интегрированную среду разработки (IDE) со встроенным редактором, механизмами поддержки проектов, различными средствами управления и анализа результатов работы отдельных инструментов.
Реальные программы обычно состоят из нескольких модулей, каждому из которых соответствует файл с исходными текстами программ (на языках высокого уровня или ассемблера). Компилятор транслирует модули на языке высокого уровня в промежуточные ассемблерные модули. Ассемблер отвечает за преобразование ассемблерных модулей (как написанных вручную, так и сгенерированных компилятором) в объектные модули с машинными кодами и данными для целевой аппаратуры. В качестве формата объектных модулей обычно используется ELF, включающий в себя различные секции (например, секции исполняемого кода, секции данных, секции с информацией о символах и секции с отладочной информацией). Компоновщик выполняет сборку нескольких объектных модулей в один абсолютный модуль с объединением соответствующих секций входных объектных файлов. При этом выполняются перемещения символов по абсолютным адресам памяти (автоматически или в соответствии с заданной программистом картой памяти) с соответствующими исправлениями зависящих от них кодов команд и значений данных. На этом заканчивается этап сборки программы. Полученный абсолютный модуль можно преобразовать в образ памяти для непосредственной загрузки в целевую аппаратуру с помощью программатора.
Для программиста взаимодействие с исполняемой моделью аппаратуры осуществляется через отладчик, который позволяет просматривать состояние модели (содержимое памяти, регистров, шин, сигналов) и осуществлять управляемое (в том числе, пошаговое) выполнение целевой программы на уровне отдельных команд или строчек исходного кода. Совместно с отладчиком используются различные виды профилировщиков и средств анализа, визуализирующих необходимые характеристики модели (как статические, так и времени исполнения) и соответствующие статистики. В качестве примеров можно привести:
- подсчет числа тактов и количества раз исполнения для каждой строчки программы (на уровне исходных кодов языка программирования высокого уровня, на уровне ассемблерных текстов и на уровне дисассемблированных команд процессора);
- визуализация графа вызовов функций и статистика затраченных для каждого узла тактов и количества раз исполнения;
- список функций программы, их размеров в программном коде и отражение количества вызовов и суммарных затрат (тактов) на их выполнение (с учетом и без учета вызова потомков);
- статистика доступа к различным областям памяти;
- статистика используемого объема памяти;
- различные статистики на уровне операционной системы (в терминах задач и примитивов синхронизации, зарегистрированных в системе).
В качестве модели целевой системы для кросс-отладчика чаще всего выступает симулятор, позволяющий моделировать целевую аппаратуру полностью на инструментальной машине. Существуют симуляторы различного уровня абстракции от функционального симулятора на уровне всей системы до симуляторов на уровне системы команд (в том числе потактово-точных) и симуляторов, эмулирующих точную структуру аппаратуры на уровне функциональных блоков и конкретных вентилей. В качестве симулятора в составе инструментария кросс-разработки обычно используется симулятор уровня системы команд (в том числе, с учетом конвейерных эффектов с потактовой точностью). Также отладчик может поддерживать отладку непосредственно аппаратной модели в виде реального чипа, подключаемой к инструментальной машине, что может иметь место на финальных стадиях проектирования и на стадии эксплуатации. Использование настоящей аппаратуры позволяет запускать программы в режиме реального времени, однако программный симулятор предоставляет гораздо больше воз можностей для отладки и анализа программ.
3.3. Разработка приложений для встроенных систем
Разработка приложений для встроенных систем - частный случай так называемой кросс-разработки. В случае кросс-разработки платформа, для которой пишется приложение (целевая платформа, например, Linux ARM в виде смартфона) отличается от платформы, на которой работает программист и создается исполняемый код для целевой платформы (платформа разработчика, например, Linux x86 в виде обычного PC). В случае ОС Android аппаратная часть платформы может быть одинакова - x86, но способ разработки все равно останется кроссплатформенный. Мы рассмотрим четыре составляющих кроссплатформенной разработки - создание исполняемого кода, его запуск, отладка и интегрированные среды разработчика.
Наиболее важной частью кроссплатформенной разработки является набор инструментов для создания исполняемого кода. В него входит компилятор, компоновщик, другие утилиты, набор библиотек для целевой платформы - этого достаточно, чтобы создать исполняемый файл для целевой платформы. Обычно этот набор инструментов является частью или близким родственником GNU Compiler Collection, то есть применяются те же названия, те же ключи командной строки, те же приемы использования. Ниже приведен пример набора инструментов для архитектуры ARM:
[r...@v...]# ls -1 toolchain/arm-eabi-4.4.0/bin/ arm-eabi-ar arm-eabi-as arm-eabi-g++ arm-eabi-gcc arm-eabi-gdb arm-eabi-gprof arm-eabi-ld arm-eabi-objdump arm-eabi-ranlib arm-eabi-strings arm-eabi-strip
Для управления процессом сборки, документирования и т.п. можно использовать те же средства, что и для обычной разработки под Linux.
Запуск объектного кода может быть осуществлен разными способами. Самый естественный - запуск непосредственно на целевом устройстве. Для этого требуется само целевое устройство, подключенное к компьютеру разработчика по USB или Ethernet. Это наиболее надежный способ, но не всегда самый удобный. Финальное тестирование необходимо производить именно на целевом устройстве.
Альтернатива - установить целевую операционную систему на универсальную виртуальную машину на платформе разработчика. В качестве такой машины для Android рекомендуется Android Virtual Device, Virtual Box, также можно использовать VMWare. При этом надо учитывать, что по некоторым параметрам виртуальная машина может отличаться от реального устройства - например, по скорости работы, особенностям периферии и т. п.
Промежуточным вариантом является использование эмулятора, предоставляемого производителем целевой платформы. Фактически это разновидность виртуальной машины, которая, с одной стороны, максимально адаптирована для эмуляции целевой платформы, а с другой стороны, интегрирована со средствами разработки, например, IDE, которые также предоставляются производителем. Примером этого может послужить Intel® HAXM.
В настоящее время для запуска обычно не ограничиваются переносом на целевую платформу одного исполняемого файла, а формируют целый пакет со всеми зависимостями и правилами установки (например, APK для Android или RPM для MeeGo), который затем устанавливается на целевой платформе. С помощью такого же пакета финальная версия приложения попадает на устройства конечных пользователей. Это, с одной стороны, увеличивает переносимость приложения и облегчает запуск сложных приложений, с другой - слегка усложняет и удлиняет процедуру запуска.
При кроссплатформенной разработке процедура отладки приложения изменяется не сильно. Описанный выше gdb можно использовать и для кросс-отладки. Для этого на целевой платформе отлаживаемое приложение запускается из-под gdbserver:
gdbserver host:port exe [args …]
На платформе разработчика запускается gdb и в его консоли устанавливается связь с gdbserver:
target remote host:port
gdb устанавливает соединение с gdbserver согласно указанным host:port и после этого программист может проводить сеанс отладки как в обычном gdb. При использовании интегрированной среды разработки все эти детали взаимодействия gdb скрыты от программиста - зачастую, глядя на экран платформы разработчика, невозможно определить, происходит отладка локально или на целевой платформе.
Еще одним популярным способом отладки при кроссплат-форменной разработке является полный отказ от gdb и интенсивное использование логов.
Современные IDE, такие как QtCreator или Eclipse, поддерживают кросс-платформенную разработку. Эта поддержка может быть встроенной или реализовываться в виде плагинов. В любом случае IDE берут на себя львиную долю организационной работы, освобождая программисту время для творчества. Внешне процесс кросс-разработки с использованием IDE практически не отличается от обычного.