Игра в имитацию: как разрабатывать и отлаживать ПО для процессора, которого нет

от автора

Разработка процессора и вообще программируемых микросхем — процесс сложный и длительный. От старта проектирования до получения первых образцов в кремнии проходит больше года. При этом ПО желательно писать и отлаживать параллельно процессу производства, чтобы оптимизировать сроки выхода продукта. Но как это делать, если железо еще не на руках или оно есть в очень ограниченном количестве, а нужно многим? 

Спойлер: делать имитацию. О том, какие подходы существуют и как выжать из них максимум эффективности для имитации сложных многоядерных систем, рассказали инженеры-программисты отдела разработки системного ПО YADRO Светлана @sita5174Бурлака и Александр Солдатов.

Как можно имитировать железо

Когда программист не может проверить работу кода в проде, он делает тестовый стенд, который «имитирует» кодовое окружение. Но что делать инженеру, который разрабатывает ПО под железо, которого нет? Как сымитировать целый процессор? 

На самом деле методы, с помощью которых можно имитировать аппаратуру частично или целиком, существуют. Бывают разные виды, их называют по-разному, но в этой статье, для упрощения, мы будем использовать два термина — симуляцию и эмуляцию. 

Между понятиями довольно тонкая грань, но условно объяснить и разделить их можно так. 

  • При симуляции имитируются свойства объекта, поведение, функции. При этом его логика и принципы работы могут игнорироваться. 

  • При эмуляции объект воспроизводится целиком (или частично) вместе со всеми принципами и логикой работы. 

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

Для каких задач мы имитируем оборудование

Все программное обеспечение, которое пишется для процессоров, требует отладки и тестирования. Для создания различных имитаций есть ряд инструментов: эмулятор QEMU, потактовые симуляторы, FPGA-платформы. Подробнее о них мы еще поговорим. 

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

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

  • тестирование функциональных моделей

    Схема эмуляции системы в QEMU.

    Эмуляция процессора осуществляется с помощью динамической двоичной трансляции и кодогенератора TCG (Tiny Code Generator). TCG берет блоки кода, переводит их сначала в машинно-независимый код, а потом в инструкции для хостовой машины. Последняя выделяет определенный объем памяти для работы QEMU. 

    В QEMU устройства и доступ к ним реализованы с помощью MMIO (Memory-mapped Input/Ouput) — ввод-вывод с отображением в памяти (это один из вариантов). Также в их реализации прописано, как реагировать на попытки обращения к памяти. Устройства регистрируются на системной шине (System bus), которая моделируется с помощью объектной модели QEMU — для работы с ней есть API.

    Устройства могут запускаться на этапе инициализации виртуальной машины, либо их можно подключать динамически с помощью QEMU Monitor, когда машина уже запущена.

    Когда нужно кастомизировать QEMU

    В работе с эмуляцией процессоров в ряде случаев стоит расширить возможности QEMU. 

    Причина 1. Если процессор работает на ядрах разной битности

    Представим, что у вас есть чип — система на кристалле, которую вы эмулируете. При этом вы используете процессорные ядра различных конфигураций: 

    • System (SYS) — группа процессорных ядер, принимающих всю нагрузку; на них запускается Linux, пользовательские приложения, они 64-битные. 

    • System Control Unit (SCU) — процессорное ядро, среди прочих функций которого — управление системой питания и загрузкой ОС. Оно 32-битное.

    Ваша задача — организовать общение между двумя системами, этого требует полноценная эмуляция. На примере питания: SYS не может себя включить и выключить самостоятельно, это делает SCU, а SYS, в свою очередь, должен попросить SCU выключить питание.

    При этом QEMU позволяет эмулировать только системы одной архитектуры. Так как у ядер чипа разная битность — читай, разная архитектура, вы не сможете запустить их в рамках одного процесса. Поэтому нужно развернуть их как две отдельные виртуальные машины QEMU и организовать связность между ними.  

    Как решить задачу

    Разработать механизм (для статьи назовем его Interconnect), благодаря которому будут общаться модели IP-блоков в системе. 

    Как может работать такой механизм, расскажем на примере модели IP-блока, представляющей собой почтовый ящик, с помощью которого две системы могут обмениваться сообщениями. В тексте назовем эту модель Mailbox. Обмен происходит через shared memory — участок памяти, расшаренный между системами, каждая может в него писать или его читать.

    Когда одна система что-то пишет в shared memory, другая система об этом не знает. Чтобы сообщить о событии из одной системы в другую, мы используем файловые дескрипторы EventFD. Они отправляют сообщения, сигнализирующие о том, что произошло какое-то событие или прерывание.

    В Linux файловый дескриптор представляет собой число — своего рода идентификатор — и контекст. Одинаковые номера EventFD не подразумевают один контекст. Так, файловый дескриптор с номером 42 в SYS может быть никак не связан с дескриптором 42 в SCU — в разных системах у них разные контексты. Таким образом, системам необходимо обмениваться файловыми дескрипторами и контекстами. Для этого можно использовать сокет, который позволяет передавать файловые дескрипторы из одной системы в другую. Этот процесс и поддерживается механизмом Interconnect.

    Вся схема может выглядеть так: 

    На этапе инициализации системы MailBox (SCU) отправляет файловые дескрипторы в Interconnect (SCU). Затем Interconnect передает эти файловые дескрипторы в Interconnect (SYS), где они отдаются в Mailbox (SYS). Системы обменялись файловыми дескрипторами — далее Interconnect не используется.

    Дальнейшее общение между SCU и SYS происходит следующим образом:

    • одна система пишет в shared memory и через EventFD отправляет сообщение о событии,

    • другая система получает сообщение о событии и идет читать shared memory.

    Все это регулируется обработчиком в главном цикле QEMU — Main Loop. Можно создать свой EventFD, зарегистрировать его в этом цикле, и QEMU будет сообщать, что сейчас пришло уведомление о каком-то событии. Далее это событие обрабатывается.

    Причина 2. IP-блоков QEMU может быть недостаточно 

    Помимо процессорных ядер, вы можете эмулировать IP-блоки чипа и периферию платформы. Часть моделей можно найти в QEMU — они разработаны сообществом. Но в более сложных случаях некоторые модели нужно будет создавать самостоятельно. 

    Функционал, который нужно внедрять в модели, может различаться. Он зависит от требований целевого программного обеспечения, которое будет взаимодействовать с IP-блоком. Иногда достаточно удовлетворить интерфейс: чтобы драйвер обнаружил устройство, зарегистрировал его, убедился, что оно включено, прочитал версию, проверил цифровую подпись. Но есть и более сложные случаи.

    Когда вы будете писать код модели и реализовывать устройство в QEMU, рекомендуем выполнить следующие действия: 

    1. Привяжите устройство к участку памяти. 

    2. Реализуйте callback-функции чтения и записи (read/write), которые будут вызываться при обращении к данному участку памяти. 

    3. Определите смещения и размеры областей MMIO, чтобы при обращении к участку памяти можно было обратиться к конкретному регистру. 

    4. Интерпретируйте чтение или запись в регистр — что именно нужно сделать. Возможна как передача данных и изменение состояния устройства, так и влияние на состояние других устройств. Последнее, например, происходит в случае с описанным ранее  Interconnect, когда одна система запускает процессы в другой. 

    В этой статье мы не будем подробно останавливаться на разработке IP-блоков. Если вам интересен процесс, пишите в комментариях. Можем развить эту тему в отдельном тексте. 

    Метод косимуляции, или когда одного способа эмуляции недостаточно

    Ранее мы перечислили три метода эмуляции процессора и уточнили, что для большей эффективности лучше использовать их все — в разных комбинациях. 

    Например, как можно проверить работу аппаратной части совместно с ПО: высокоскоростные ядра запускаются на быстром эмуляторе, а тестируемый контроллер — на потактовом симуляторе или FPGA-платформе. Таким образом можно эффективно проверить работу драйвера с устройством.

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

    Можно выделить два вида косимуляции. 

    Программная: QEMU + потактовый симулятор. В рамках такой косимуляции можно, запустив Linux на QEMU, отлаживать драйверы IP-блоков, которые реализованы на RTL и выполняются внутри потактового симулятора. 

    Программно-аппаратная: QEMU + FPGA-платформа. В этом случае можно подключить к системе внутри QEMU внешнее устройство — например, дисплей. Как это выглядит: на QEMU запускаем Linux, а видеоконтроллер реализуем внутри FPGA, к которой подключен экран.

    Аппаратно-программная косимуляция — тема для отдельной подробной статьи. В этом тексте мы больше расскажем, как реализовать программную косимуляцию. 

    Разработка программной косимуляции

    Итак, наша задача — связать два симулятора, то есть через какую-то «магию» заставить их общаться между собой. Эту «магию» можно реализовать через открытую технологию Remote Port TLM — создана компанией Xilinx и доступна под лицензией MIT. 

    Разработка ориентирована на применение языка SystemC и связанной с ним технологии TLM. Фактически она позволяет на языке С++ описывать взаимодействие QEMU и потактового симулятора.

    Как работает SystemC Transaction Level Modeling (TLM)

    Технология SystemC TLM.

    Технология SystemC TLM.

    Есть QEMU с добавленным псевдоустройством для того, чтобы общаться с Remote Port. В оригинальной технологии, описанной на картинке выше, используется связка SystemC TLM, где есть:

    • бридж, преобразующий Remote Port-вызов в транзакции SystemC TLM,

    • Технология SystemVerilog DPI.

      Технология SystemVerilog DPI.

      В тестовой реализации можно организовать взаимодействие QEMU через механизм RemotePort с кодом SystemVerilog внутри симулятора.  

      QEMU Remote Port.

      QEMU Remote Port.

      Обычно QEMU выступает в роли мастера: отправляет AXI-транзакции до модели периферии в потактовый симулятор и дает ей возможность должным образом ответить. Для этого на стороне QEMU есть псевдоустройство — RP Master, который переадресует обращения в память устройства, преобразовывая их в транзакции Remote Port. Когда QEMU является мастером на AXI-шине, организовать общение с потактовым симулятором намного проще. 

      Однако существуют ситуации, когда эмулятор работает как slave — мастером выступает модель, выполняемая в потактовом симуляторе. Ей нужна возможность отправлять транзакции в QEMU. Когда такое может произойти? Например, внутри потактового симулятора у нас выполняется модель видеоконтроллера, которая должна самостоятельно ходить в память QEMU (прямой доступ в память DMA) и забирать изображения, которые она будет показывать. В таком случае устройство одновременно и slave (мастер — процессор), и мастер (slave для него — оперативная память). 

      Когда QEMU — slave, общение организовать сложнее. Для этого на версию эмулятора нужно портировать псевдоустройства RP slave и RP wire (на иллюстрации выше) — последнее участвует в работе Interrupt controller, который обеспечивает прерывание работы процессора. Оба псевдоустройства осуществляют преобразование пакетов в транзакции в память гостевой системы в QEMU — все это, соответственно, передается в сокет. 

      Remote Port проприетарного симулятора.

      Remote Port проприетарного симулятора.

      Для взаимодействия QEMU и потактового симулятора можно создать библиотеку на базе стандарта SystemVerilog DPI. Она преобразует транзакции RemotePort в вызовы SystemVerilog DPI и обратно, то есть реализует Remote Port to DPI.

      Заключение

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

      Для имитации сложных систем лучше всего работает метод косимуляции, благодаря которому можно нивелировать недостатки отдельных подходов к симуляции процессора и его окружения. Несмотря не сложность реализации, косимуляция окупается тем, что экономит ресурсы и время команды. Можно не развертывать всю систему внутри FPGA-платформы, а раскрутить в ней один или несколько IP-блоков и просимулировать их совместно с основной системой, которая работает на х86-хосте в QEMU. 

      Приходилось ли вам имитировать железо и другие системы? Как справлялись с задачей? Пишите в комментариях.


ссылка на оригинал статьи https://habr.com/ru/articles/776252/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *