hardware/emulator/bus_emulators.html · hardware/emulator/bus_emulators.rst 공식 원문 ↗

Zephyr 3.7.0 · Hardware · Emulator · Bus

External bus와 peripheral emulator

실제 hardware 없이 bus와 peripheral driver의 정상·실패 경로를 끝까지 시험하는 구조를 설명합니다.

Official pathhardware/emulator/bus_emulators.html
Source filehardware/emulator/bus_emulators.rst
Source versionZephyr 3.7.0
TranslationFull · reviewed

본문 기준: Zephyr 3.7.0 LTS3 · 이후 유지보수 패치는 별도입니다.

Part 1

요약·해설

native_sim, bus_api, backend_api를 결합해 driver와 application test coverage를 높이는 방법입니다.

Buses

I2C · SPI · eSPI · MSPI

Interfaces

bus_api · backend_api

Testing

native_sim · failure injection

Part 2

접을 수 있는 영어 원문 전체

영어 원문 전체 펼치기
원문 SHA-256 C3D561A60A4F1387F1CA5FCDC773670B79B6C58E1F58FD90BDF134C81A9A1307
.. _bus_emul:

External Bus and Bus Connected Peripherals Emulators
####################################################

Overview
========

Zephyr supports a simple emulator framework to support testing of external peripheral drivers
without requiring real hardware.

Emulators are used to emulate external hardware devices, to support testing of
various subsystems. For example, it is possible to write an emulator
for an I2C compass such that it appears on the I2C bus and can be used
just like a real hardware device.

Emulators often implement special features for testing. For example a
compass may support returning bogus data if the I2C bus speed is too
high, or may return invalid measurements if calibration has not yet
been completed. This allows for testing that high-level code can
handle these situations correctly. Test coverage can therefore
approach 100% if all failure conditions are emulated.

Concept
=======

The diagram below shows application code / high-level tests at the top.
This is the ultimate application we want to run.

.. figure:: ../../../../hardware/emulator/img/arch.svg
   :align: center
   :alt: Emulator architecture showing tests, emulators and drivers

Below that are peripheral drivers, such as the AT24 EEPROM driver. We can test
peripheral drivers using an emulation driver connected via a emulated I2C
controller/emulator which passes I2C traffic from the AT24 driver to the AT24
simulator.

Separately we can test the STM32 and NXP I2C drivers on real hardware using API
tests. These require some sort of device attached to the bus, but with this, we
can validate much of the driver functionality.

Putting the two together, we can test the application and peripheral code
entirely on native_sim. Since we know that the I2C driver on the real hardware
works, we should expect the application and peripheral drivers to work on the
real hardware also.

Using the above framework we can test an entire application (e.g. Embedded
Controller) on native_sim using emulators for all non-chip drivers.

With this approach we can:

* Write individual tests for each driver (green), covering all failure modes,
  error conditions, etc.

* Ensure 100% test coverage for drivers (green)

* Write tests for combinations of drivers, such as GPIOs provided by an I2C GPIO
  expander driver talking over an I2C bus, with the GPIOs controlling a charger.
  All of this can work in the emulated environment or on real hardware.

* Write a complex application that ties together all of these pieces and runs on
  native_sim. We can develop on a host, use source-level debugging, etc.

* Transfer the application to any board which provides the required features
  (e.g. I2C, enough GPIOs), by adding Kconfig and devicetree fragments.

Creating a Device Driver Emulator
=================================

The emulator subsystem is modeled on the :ref:`device_model_api`.  You create
an emulator instance using one of the :c:func:`EMUL_DT_DEFINE()` or
:c:func:`EMUL_DT_INST_DEFINE()` APIs.

Emulators for peripheral devices reuse the same devicetree node as the real
device driver. This means that your emulator defines `DT_DRV_COMPAT` using the
same ``compat`` value from the real driver.

.. code-block:: C

  /* From drivers/sensor/bm160/bm160.c */
  #define DT_DRV_COMPAT bosch_bmi160

  /* From drivers/sensor/bmi160/emul_bmi160.c */
  #define DT_DRV_COMPAT bosch_bmi160

The ``EMUL_DT_DEFINE()`` function accepts two API types:

  #. ``bus_api`` - This points to the API for the upstream bus that the emulator
     connects to. The ``bus_api`` parameter is required.  The supported
     emulated bus types include I2C, SPI, eSPI, and MSPI.
  #. ``_backend_api`` - This points to the device-class specific backend API for
     the emulator. The ``_backend_api`` parameter is optional.

The diagram below demonstrates the logical organization of the ``bus_api`` and
``_backend_api`` using the BC1.2 charging detector driver as the model
device-class.

.. figure:: ../../../../hardware/emulator/img/device_class_emulator.svg
   :align: center
   :alt: Device class example, demonstrating BC1.2 charging detectors.

The real code is shown in green, while the emulator code is shown in yellow.

The ``bus_api`` connects the BC1.2 emulators to the ``native_sim`` I2C
controller. The real BC1.2 drivers are unchanged and operate exactly as if there
was a physical I2C controller present in the system. The ``native_sim`` I2C
controller uses the ``bus_api`` to initiate register reads and writes to the
emulator.

The ``_backend_api`` provides a mechanism for tests to manipulate the emulator
out of band.  Each device class defines it's own API functions.  The backend API
functions focus on high-level behavior and do not provide hooks for specific
emulators.

In the case of the BC1.2 charging detector the backend API provides functions
to simulate connecting and disconnecting a charger to the emulated BC1.2 device.
Each emulator is responsible for updating the correct vendor specific registers
and potentially signalling an interrupt.

Example test flow:

  #. Test registers BC1.2 detection callback using the Zephyr BC1.2 driver API.
  #. Test connects a charger using the BC1.2 emulator backend.
  #. Test verifies B1.2 detection callback invoked with correct charger type.
  #. Test disconnects a charger using the BC1.2 emulator backend.

With this architecture, the same test can be used will all supported drivers in
the same driver class.

Available Emulators
===================

Zephyr includes the following emulators:

* I2C emulator driver, allowing drivers to be connected to an emulator so that
  tests can be performed without access to the real hardware

* SPI emulator driver, which does the same for SPI

* eSPI emulator driver, which does the same for eSPI. The emulator is being
  developed to support more functionalities.

* MSPI emulator driver, allowing drivers to be connected to an emulator so that
  tests can be performed without access to the real hardware.

Samples
=======

Here are some examples present in Zephyr:

#. Bosch BMI160 sensor driver connected via both I2C and SPI to an emulator:

   .. zephyr-app-commands::
      :app: tests/drivers/sensor/accel/
      :board: native_sim
      :goals: build

#. The same test can be built with a second EEPROM which is an Atmel AT24 EEPROM driver
   connected via I2C an emulator:

   .. zephyr-app-commands::
      :app: tests/drivers/eeprom/api
      :board: native_sim
      :goals: build
      :gen-args: -DDTC_OVERLAY_FILE=at2x_emul.overlay -DOVERLAY_CONFIG=at2x_emul.conf

API Reference
*************

.. doxygengroup:: io_emulators

Part 3

한국어 전문 번역

개요

Zephyr는 실제 hardware 없이 외부 peripheral driver를 시험할 수 있는 간단한 emulator framework를 지원합니다.

Emulator는 외부 hardware device를 흉내 내 여러 subsystem의 시험을 돕습니다. 예를 들어 I2C compass emulator를 만들면 실제 compass처럼 I2C bus에 나타나 동일한 방식으로 사용할 수 있습니다.

Emulator에는 시험용 특수 동작을 넣을 수 있습니다. I2C bus 속도가 너무 빠르면 compass가 잘못된 data를 돌려주게 하거나 calibration이 끝나지 않았을 때 유효하지 않은 measurement를 반환하게 할 수 있습니다. 그러면 상위 code가 이런 상황을 올바르게 처리하는지 검증할 수 있고, 모든 failure condition을 emulation하면 test coverage를 100%에 가깝게 높일 수 있습니다.

개념

다음 구조도 맨 위에는 최종적으로 실행하려는 application code와 high-level test가 있습니다.

Test, emulator, peripheral driver와 실제 bus driver를 함께 보여 주는 emulator architecture
Application test, emulated peripheral·bus, 실제 hardware driver를 연결하는 emulator architecture.

그 아래에는 AT24 EEPROM 같은 peripheral driver가 있습니다. AT24 driver의 I2C traffic을 AT24 simulator로 전달하는 emulated I2C controller/emulator에 emulation driver를 연결하면 peripheral driver를 시험할 수 있습니다.

별도로 실제 hardware에서 API test를 실행해 STM32와 NXP I2C driver를 시험할 수 있습니다. Bus에 device를 연결해야 하지만 이를 통해 driver 기능의 상당 부분을 검증합니다.

두 결과를 결합하면 application과 peripheral code 전체를 native_sim에서 시험할 수 있습니다. 실제 hardware의 I2C driver가 동작함을 이미 확인했으므로 application과 peripheral driver도 실제 hardware에서 동작할 것으로 기대할 수 있습니다.

이 framework를 이용하면 Embedded Controller 같은 전체 application을 native_sim에서 실행하고 chip 외부 driver를 모두 emulator로 대체할 수 있습니다.

이 접근으로 할 수 있는 작업은 다음과 같습니다.

  • 각 driver의 모든 failure mode와 error condition을 포함하는 개별 test를 작성합니다.
  • Driver test coverage 100%를 목표로 할 수 있습니다.
  • I2C GPIO expander가 제공한 GPIO가 charger를 제어하는 경우처럼 여러 driver 조합을 시험할 수 있습니다. Emulated 환경과 실제 hardware 모두에서 같은 구성을 실행할 수 있습니다.
  • 모든 구성 요소를 묶는 복잡한 application을 native_sim에서 실행하면서 host에서 개발하고 source-level debugging을 사용할 수 있습니다.
  • 필요한 기능(I2C, 충분한 GPIO 등)을 제공하는 board라면 Kconfig와 devicetree fragment를 추가해 application을 옮길 수 있습니다.

Device driver emulator 만들기

Emulator subsystem은 Zephyr device model을 본떠 만들었습니다. EMUL_DT_DEFINE() 또는 EMUL_DT_INST_DEFINE() API로 emulator instance를 생성합니다.

Peripheral device emulator는 실제 device driver와 같은 devicetree node를 재사용합니다. 따라서 emulator의 DT_DRV_COMPAT는 실제 driver의 compat 값과 같아야 합니다.

/* From drivers/sensor/bm160/bm160.c */
#define DT_DRV_COMPAT bosch_bmi160

/* From drivers/sensor/bmi160/emul_bmi160.c */
#define DT_DRV_COMPAT bosch_bmi160

EMUL_DT_DEFINE() 함수는 두 API type을 받습니다.

  1. bus_api: emulator가 연결되는 upstream bus API를 가리키며 필수입니다. 지원하는 emulated bus는 I2C, SPI, eSPI, MSPI입니다.
  2. _backend_api: emulator의 device class별 backend API를 가리키며 선택 항목입니다.

다음 구조도는 BC1.2 charging detector driver를 model device class로 삼아 bus_api와 _backend_api의 논리 구성을 보여 줍니다.

BC1.2 charging detector를 예로 든 bus API와 backend API 구성
BC1.2 device class에서 실제 driver와 emulator bus·backend API가 만나는 구조.

그림에서 실제 code는 녹색, emulator code는 노란색입니다.

bus_api는 BC1.2 emulator를 native_sim I2C controller에 연결합니다. 실제 BC1.2 driver는 변경되지 않고 system에 물리 I2C controller가 있는 것처럼 동작합니다. native_sim I2C controller는 bus_api로 emulator register read/write를 시작합니다.

_backend_api는 test가 bus traffic 밖의 경로로 emulator를 조작하게 합니다. Device class마다 자체 API 함수를 정의하며, backend API는 high-level behavior에 집중하고 특정 emulator에만 해당하는 hook은 제공하지 않습니다.

BC1.2 charging detector의 backend API는 emulated BC1.2 device에 charger를 연결하거나 분리하는 함수를 제공합니다. 각 emulator는 올바른 vendor-specific register를 갱신하고 필요하면 interrupt를 signal해야 합니다.

시험 흐름 예시는 다음과 같습니다.

  1. Zephyr BC1.2 driver API를 사용해 BC1.2 detection callback을 등록합니다.
  2. BC1.2 emulator backend를 사용해 charger를 연결합니다.
  3. 올바른 charger type과 함께 BC1.2 detection callback이 호출됐는지 확인합니다.
  4. BC1.2 emulator backend를 사용해 charger를 분리합니다.

이 architecture에서는 같은 driver class에 속한 모든 지원 driver에 동일한 test를 사용할 수 있습니다.

사용 가능한 emulator

Zephyr에는 다음 bus emulator가 포함됩니다.

  • I2C emulator driver: 실제 hardware 없이 test할 수 있도록 driver를 emulator에 연결합니다.
  • SPI emulator driver: SPI에 대해 같은 기능을 제공합니다.
  • eSPI emulator driver: eSPI를 emulation하며 더 많은 기능을 지원하도록 개발 중입니다.
  • MSPI emulator driver: 실제 hardware 없이 MSPI driver를 emulator에 연결해 시험합니다.

Sample

Zephyr에 포함된 예시는 다음과 같습니다.

  1. Bosch BMI160 sensor driver를 I2C와 SPI 양쪽으로 emulator에 연결합니다.
  2. 같은 test에 Atmel AT24 EEPROM driver인 두 번째 EEPROM을 I2C emulator로 연결합니다.
west build -b native_sim tests/drivers/sensor/accel/
west build -b native_sim tests/drivers/eeprom/api -- -DDTC_OVERLAY_FILE=at2x_emul.overlay -DOVERLAY_CONFIG=at2x_emul.conf

API reference

io_emulators Doxygen group은 external bus와 bus-connected peripheral emulator API를 제공합니다.

Source

출처

영어 원문에는 문단, 문서 지시문, 표, 코드, 코드 심벌, 경로를 그대로 보존했습니다.