Facades and Backends

The SPARK SDK employs a facade-backend architecture to create an interface for the underlying hardware-specific implementations. This article outlines the roles of facades and backends and how to effectively work with them in the context of SPARK’s SDK.

Facades

Purpose and Design: Facades serve as interfaces for platform-specific features such as SPI communication, IRQ handling, timer functions, and context switching mechanisms. By encapsulating the functionality of low-level drivers within a single header file, the facade creates a cohesive API that can be seamlessly ported to various hardware platforms via their backends.

Usage Example: When using the SPARK Wireless Core library, the developer interacts solely with the facade interfaces which act as a gateway to the backend functionalities. This provides a consistent API, regardless of the underlying hardware.

Backends

Platform-specific Implementations: The backends are the implementations of the functionalities declared by the facades. They are written to cater to the specifics of the target platform, ensuring that the SPARK Cores and other components, such as the example apps and modules, can easily be ported to user-specific hardware.

Working with Facades and Backends

The SDK provides a common_facade and a common_backend to simplify development and porting.

  • common_facade: This header file declares a set of common functions that are shared across all applications and examples, such as facade_delay() and facade_print_string(). It establishes a baseline API that any application built with the SPARK SDK can expect to be available.

  • common_backend: This source file provides default implementations for the functions declared in the common_facade. These functions are defined with the __weak attribute, allowing them to be overridden. This design ensures that a project can compile and link successfully with baseline functionality, while giving developers the flexibility to provide their own “strong” (non-weak) implementations to add custom or hardware-specific features.

When using the SPARK Wireless Core library, for instance, the developer interacts solely with the facade interfaces, which act as a gateway to the backend functionalities. This provides a consistent API, regardless of the underlying hardware.

Developing Custom Backends:

  • To support a new hardware platform, developers must implement the backend for each facade used by components included in the user project.

  • Implementations should adhere to the definitions provided by the facade interfaces.

Integrating with the Build System:

  • The build system should be configured to include the appropriate backend implementation files based on the target platform for which the application is being compiled.

Testing and Validation:

  • Each backend implementation must be tested to ensure that it conforms to the facade’s API.

  • Developers can utilize the BSP Validator tool to validate the correctness of the backend implementations for the SPARK Wireless Core.

By understanding and applying the facade-backend architecture, developers can create applications with the SPARK components that are portable across a multitude of hardware platforms.

List of Components with Facades

SDK Example Apps

  • Common

  • Audio Bidirectional

  • Audio Unidirectional

  • Connection Priority

  • Datalink

  • Fragmentation

  • Hello World

  • Hello World RTOS

  • Star Network

Cores

  • Wireless core

  • Audio core

Modules & Middleware

  • TinyUSB Middleware

  • TinyUSB Baremetal module

  • TinyUSB FreeRTOS module

SDK Tools

  • BSP Validator

  • Profiler

Hello-World Porting Example

If a developer wishes to port the Hello-World example they will need to implement the backends for the application facade and its dependencies. In this instance, this involves implementing two backends: one for the Hello-World facade and another for the Wireless Core facade.

Each facade is located in its respective component under the facade/ folder.

Using Common Functions

Developers can include the common_facade.h header file, located in the app/example/common folder, in the application facade header file to leverage prebuilt common functions. This approach simplifies development by providing access to standardized functionality. For example:

#include "common_facade.h"

If developers prefer not to use the prebuilt common functions, they should implement their own equivalents tailored to their specific requirements.

Implementing Backends

The backend implementation can be placed anywhere; it does not need to reside within the component folder. We recommend that developers create their backend implementations external to the SDK. Users can integrate their backends by adding their source files to the component_name_backend CMake library.

target_sources(hello_world_backend PRIVATE user_backend.c)

To add extra dependencies to the CMake library, like a user board support package, the user can link libraries.

target_link_libraries(hello_world_backend PRIVATE user_bsp)

In the SDK, SPARK has organized all backend implementations into a dedicated folder. This organization demonstrates that backend implementations do not need to be situated adjacently to their facade or code component. We advise users to refrain from adding code directly to the SDK package. Due to the CMake architecture and approach, the backend implementation can be seamlessly integrated into the SDK from the user’s code base.

CMake Architecture and Dependencies

Given that the creation of a user-specified top-level CMakeLists.txt file is encouraged, it is crucial to take into account the dependencies of the SDK’s underlying components. An overview of the CMake library architecture and build sequence within the SPARK SDK, which minimizes inter-dependencies between components, is provided below and is organized from the first to be included to the last:

  1. SPARK Libraries and Third-Party Libraries: SPARK’s own libraries and those from third parties.

  2. Driver: This layer includes all the drivers necessary for hardware interaction.

  3. Board Support Package (BSP): This package provides all the necessary support for board-specific functions and initialization.

  4. Middleware: Middleware components act as intermediaries, offering common services and features for higher-level software.

  5. Core: The core layer contains essential libraries and frameworks that form the backbone of the SDK.

  6. Modules: These are discrete functional units that provide specific capabilities or services within the system.

  7. Applications: This final layer includes example applications, BSP-validator, demo, and other application-level projects.

The inclusion rules are structured such that each level can include and depend on components from lower levels, but must not include components from higher levels. This hierarchical structuring ensures clarity and minimizes dependency conflicts across the SDK.