1 April 2016

Разработка для embedded-систем

Небольшое предупреждение: в рамках этого разговора под "embedded" подразумевается не совсем то, что принято называть. Сейчас Internet of Things (IoT) это очень быстрорастущая и развивающаяся область и разработчики часто подразумевают под ней модули вроде esp8226 и различные --duino платы.

Сегодня же я буду говорить о системах-на-кристалле (SoC), используемых в ГНСС. У них тоже очень низкое энергопотребление, относительно маломощные процессоры (ARM или PowerPC), зато они часто поставляются вместе в цифровыми сигнальными процессорами (DSP), что оказывается очень кстати в ситуации с ограниченными вычислительными мощностями.

В идеальном мире вся разница между разработкой для ПК и ГНСС-приёмников, которая видна программисту, должна заключаться в различии производительности. Но всё не так просто. Необходимо скачать сторонний тулчейн, новую IDE, купить JTAG-отладчик (жутко дорогой, кстати говоря) и т.д. И это ещё не всё: поскольку мы сменили компилятор, необходимо аккуратно пересмотреть весь написанный код. Больше нет возможностей C++11 (пока шаблоны, auto, nullptr), помимо этого я ещё не встречал SoC с реализованной стандартной библиотекой (больше никаких std::vector, только обычные статические массивы). Мне очень нравится идея, которую пропагандирует Microsoft с Windows 10, что есть одна общая ОС и одни средства разработки для всех устройств: компьютеров, смартфонов и планшетов. Но опять, мы живём не в идеальном мире. Так вот, не стоит верить компилятору. Обязательно стоит проверить объектный файл, перепроверить дважды, если есть какой-то баг, или существует некая неуверенность. Чем более узконаправлен компилятор, тем больше в нём скрытых багов и поведения, которого от него не ожидаешь. Мораль сей басни такова: не стоит верить компилятору!

Чтобы запустить приложение на голом железе необходимо его разместить по "нулевому адресу". После чего первое немаскируемое прерывание запустит программу. Звучит просто, не правда ли?

Сегодня я узнал, что не правда. Этот метод предполагает, что первая инструкция расположена ровно по 0x0, но я повторю опять: не стоит верить компилятору. Он может перемешать секцию кода, данных и другие как ему заблагорассудится и как он сочтёт нужным. А самое худшее в этом, что он даже ничего не скажет об этом, поскольку по внутреннему алгоритму сочтёт это оптимальным. Сегодня мне пришлось для борьбы с этим использовать костыль: ассемблерная функция, которая находится в отдельной секции и располагается ровно по нулевому адресу. Эта функция (pre_start) просто вызывает метку start, которая состоит из различных инициализаций, генерируемых компилятором. После этой функции start вызывается функция main() из C/C++ кода и мы можем начать отладку на голом железе.

Приложение на ARM, которое я отлаживал, сейчас занимается только контролем двух светодиодов, для включения которых мне необходимо установить соответствующие этим светодиодам уровни на GPIO в 0... Но это уже совсем другая история про отдел, который занимается разработкой плат.

P.S. Когда я говорю не верить компилятору я не апеллирую к голубой мечте всех программистов родом из стран бывшего СССР, которые все как один хотят написать свой компилятор. Я предлагаю писать код, который будет транслирован очевидным образом и, если этой возможности нет, перепроверить дизассемблированный код.

No comments:

Post a Comment