Функции заглушки в симуляциях
-
03-07-2019 - |
Вопрос
Я работаю над проектом встроенного C, который зависит от некоторого внешнего HW. Я хотел бы заглушить код доступа к этим частям, чтобы я мог смоделировать систему без использования HW. До сих пор я использовал некоторые макросы, но это заставляет меня немного изменить свой рабочий код, чего я бы хотел избежать.
Пример:
stub.h
#ifdef _STUB_HW
#define STUB_HW(name) Stub_##name
#else /*_STUB_HW*/
#define STUB_HW(name) name
#endif /*_STUB_HW*/
my_hw.c
WORD STUB_HW(clear_RX_TX)()
{ /* clear my rx/tx buffer on target HW */ }
test_my_hw.c
#ifdef _STUB_HW
WORD clear_RX_TX()
{ /* simulate clear rx/tx buffer on target HW */ }
С помощью этого кода я могу включить / выключить заглушку с помощью тега препроцессора _STUB_HW
Есть ли способ сделать это, не меняя код моего продукта и избегая множества ifdefs. И я не буду смешивать prod и test code в одном файле, если смогу избежать этого. Мне все равно, как выглядит тестовый код, если я могу максимально сохранить его от производственного кода.
Изменить:
Было бы неплохо, если бы можно было выбирать / переименовывать функции без замены всего файла. Например, возьмите все функции, начиная с nRF _ ##
и давая затем новое имя, а затем вставляя test_nRF _ ##
в nRF _ ##
, если это возможно р>
Решение
Я просто создаю два файла ActualDriver.c и StubDriver.c, которые содержат абсолютно одинаковые имена функций. При создании двух сборок, связывающих производственный код с различными объектами, нет конфликтов имен. Таким образом, производственный код не содержит тестовый или условный код.
Другие советы
Как сказал Герхард, используйте общий заголовочный файл " driver.h " и отдельные файлы реализации аппаратного уровня, содержащие фактические и заглушенные функции.
В затмении у меня есть две цели, и я "исключаю из сборки" файл driver.c, который не должен использоваться, и убедитесь, что нужный файл включен в сборку. Затем Eclipse генерирует make-файл во время сборки.
Еще одна проблема, на которую следует обратить внимание, - убедиться, что вы определяете целые числа фиксированного размера, чтобы ваш код вел себя одинаково с точки зрения переполнения. (Хотя из вашего примера кода я вижу, что вы это делаете.)
Я согласен с вышеизложенным. Стандартным решением этого является определение непрозрачного абстрагированного набора вызовов функций, которые являются «драйвером». на HW, а затем вызвать это в основной программе. Затем предоставьте две разные реализации драйвера, одну для hw, одну для sw. Вариант sw будет соответствующим образом имитировать IO-эффект hw.
Обратите внимание, что если цель находится на более низком уровне, т. е. написание кода, в котором должен быть смоделирован каждый аппаратный доступ, а не целые функции, это может быть немного сложнее. Но здесь разные "write_to_memory" и " read_from_memory " функции (или макросы, если важна скорость на цели).
В любом случае нет необходимости изменять имена функций, достаточно иметь два разных пакетных файла, создавать файлы или создавать целевые объекты IDE (в зависимости от того, какие инструменты вы используете). Р>
Наконец, во многих случаях лучшим техническим решением является использование полнофункционального симулятора целевой системы, такого как Qemu. , Simics , SystemC , CoWare , VaST или аналогичный. Это позволяет вам постоянно выполнять один и тот же код, а вместо этого вы строите модель аппаратного обеспечения, которая работает как фактическое аппаратное обеспечение с точки зрения программного обеспечения. Это требует гораздо больших первоначальных инвестиций, но для многих проектов оно того стоит. Это в основном избавляет от неприятной проблемы наличия разных сборок для цели и хоста и гарантирует, что вы всегда используете свой кросс-компилятор с опциями сборки развертывания. Обратите внимание, что многие встроенные комплекты компиляторов поставляются с некоторыми базовыми встроенными возможностями моделирования.