Вопрос

Я работаю над проектом встроенного 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 или аналогичный. Это позволяет вам постоянно выполнять один и тот же код, а вместо этого вы строите модель аппаратного обеспечения, которая работает как фактическое аппаратное обеспечение с точки зрения программного обеспечения. Это требует гораздо больших первоначальных инвестиций, но для многих проектов оно того стоит. Это в основном избавляет от неприятной проблемы наличия разных сборок для цели и хоста и гарантирует, что вы всегда используете свой кросс-компилятор с опциями сборки развертывания. Обратите внимание, что многие встроенные комплекты компиляторов поставляются с некоторыми базовыми встроенными возможностями моделирования.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top