Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions docs/bm_osal_hal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# OSAL and HAL Abstraction Layers in Bristlemouth

This document explains the design, rationale, and usage of the OS Abstraction Layer (OSAL) and Hardware Abstraction Layer (HAL) introduced into the bm_protocol codebase.

These abstraction layers are intended to improve portability, testability, and maintainability of the Bristlemouth firmware stack. This will also improve code quality and flexibility.

Contributors should use OSAL and HAL APIs instead of direct FreeRTOS or STM32 calls when possible.

For more details, refer to:

[src/lib/bm_osal/README.md](../src/lib/bm_osal/README.md)

[src/lib/bm_hal/README.md](../src/lib/bm_hal/README.md)

# Design Goals

The OSAL and HAL provide a uniform interface between Bristlemouth core libraries and the underlying operating system and hardware platform. This decouples application logic from specific platform implementations (e.g., FreeRTOS, STM32), allowing for:

* **Portability**:

* Bristlemouth firmware can compile and run on multiple targets (FreeRTOS, host, test harnesses)

* Easier to port Bristlemouth to new platforms or RTOSs


* **Testability**:
* Core logic can be tested without running on embedded hardware

* Easier unit testing using mocks or POSIX on host systems

* **Separation of concerns**:
* Cleaner dependency boundaries and encapsulation

* Break tight coupling and dependency on specific RTOS's and target chips

* Platform-specific logic (e.g., STM32 hal, FreeRTOS) is hidden behind well-defined APIs

* **Minimal overhead**:
* All abstractions are implemented as thin wrappers around native RTOS or hardware calls

# Abstraction Layers

## OSAL (src/lib/bm_osal)

The OSAL defines common interfaces for RTOS features:

* Task creation and delays

* Mutexes

* Queues

* Timers

* Millisecond delays

### Implementations

* bm_osal_freertos.c – wraps FreeRTOS primitives

* bm_osal_mock.c – dummy mock layer for unit testing

* bm_osal_posix.c – pthread-based stub for POSIX systems

## HAL (src/lib/bm_hal)

The HAL provides hardware-related abstractions, currently:

* Watchdog instance access and reload

* Watchdog IRQ handler (STM32-specific)

This layer is intended to expand to other peripherals such as GPIO or UART.

### Implementations

* bm_hal_stm32u5xx.c – STM32 IWDG-based watchdog

# Build Configuration

The following CMake variables determine which implementation to use:

```
cmake -D BM_OSAL=freertos # or mock, posix
cmake -D BM_HAL_TARGET=stm32u5xx
```

# Usage Example

## Example use of OSAL
```
#include "bm_osal.h"

void app_task(void* arg) {
while (1) {
// do work
bm_osal_task_delay(100);
}
}

void start_app() {
bm_osal_task_handle_t handle;
bm_osal_task_create(app_task, "app", 256, NULL, 1, &handle);
}
```

## Example use of HAL
```
#include "bm_hal.h"

void watchdogFeed() {
bm_hal_wd_reload_counter(bm_hal_wd_get_handle());
}
```

# Future Work

* **bm_hal**
* Abstract additional hardware peripherals (GPIO, UART, SPI)

* Support more targets

* Add unit testing

* Currently, the API's mirror the ST hal. This is a good first step to break dependencies. But these apis' would likely need to change to accomodate other targets in the future.

* **bm_osal**
* Add support for stream buffers or event groups

* Add more unit testing

* Expand POSIX implementation

* Currently, the API's mirror FreeRTOS. This is a good first step to break dependencies. But these apis' would likely need to change to accomodate other RTOS's in the future.

# Limitations

* Not all FreeRTOS features are abstracted (e.g., stream buffers still used directly)

* Did not test on any actual platforms yet

* Unit tests only cover task abstraction; others are stubbed
8 changes: 8 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ if (NOT DEFINED APP)
return()
endif()

if (NOT DEFINED BM_OSAL)
set(BM_OSAL "freertos")
endif()

if (NOT DEFINED BM_HAL_TARGET)
set(BM_HAL_TARGET "stm32u5xx")
endif()

# Default to USE_BOOTLOADER unless otherwise set
if (NOT USE_BOOTLOADER STREQUAL 0 AND NOT APP STREQUAL "bootloader")
set(USE_BOOTLOADER 1)
Expand Down
25 changes: 25 additions & 0 deletions src/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,29 @@ set(LIB_FILES
${BM_INTEGRATION_FILES}
)

if(BM_OSAL MATCHES "freertos")
list(APPEND LIB_FILES
${LIB_DIR}/bm_osal/bm_osal_freertos.c
)
elseif(BM_OSAL MATCHES "posix")
list(APPEND LIB_FILES
${LIB_DIR}/bm_osal/bm_osal_posix.c
)
elseif(BM_OSAL MATCHES "mock")
list(APPEND LIB_FILES
${LIB_DIR}/bm_osal/bm_osal_mock.c
)
else()
message(SEND_ERROR "OSAL Target is not specified via environment variable BM_OSAL")
return()
endif()

if(BM_HAL_TARGET MATCHES "stm32u5xx")
list(APPEND LIB_FILES
${LIB_DIR}/bm_hal/bm_hal_stm32u5xx.c
)
endif()

if(APP_NAME MATCHES "bridge" AND
NOT APP_NAME MATCHES "bringup" AND
NOT APP_NAME MATCHES "serial")
Expand Down Expand Up @@ -145,6 +168,8 @@ endif()
set(LIB_INCLUDES
${LIB_DIR}/bristlefin
${LIB_DIR}/bm_integration
${LIB_DIR}/bm_hal
${LIB_DIR}/bm_osal
${LIB_DIR}/bridge
${LIB_DIR}/cli
${LIB_DIR}/common
Expand Down
87 changes: 87 additions & 0 deletions src/lib/bm_hal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Bristlemouth HAL - Hardware Abstraction Layer

This directory contains the Hardware Abstraction Layer (HAL) for Bristlemouth firmware.

It abstracts platform-specific hardware behavior behind a common interface, allowing system-level code to remain portable and hardware-agnostic.

# Purpose

* Provide clean, minimal access to platform hardware (e.g., watchdog)

* Allow alternate hardware targets to implement the same interfaces

* Encourage separation between hardware access and business logic

# Current Scope

The HAL currently defines a simple Watchdog API:

* `bm_hal_wd_get_handle()` – returns opaque watchdog handle

* `bm_hal_wd_reload_counter()` – reloads (kicks) the watchdog

* `bm_hal_wd_log()` – optional hook for diagnostics (e.g., Memfault)

# Supported Targets

* `stm32u5xx` – STM32 HAL-based implementation using IWDG

# Directory Structure

```
bm_hal/
├── bm_hal.h # Public HAL interface
└── bm_hal_stm32u5xx.c # stm32u5xx watchdog implementation
```

# CMake Configuration

Specify the hardware target using:

```
cmake -DBM_HAL_TARGET=stm32u5xx ..
```

If this parameter is not specified, the default will be `stm32u5xx`.

# Usage Example

```
#include "bm_hal.h"

bm_hal_wd_t wd = bm_hal_wd_get_handle();
if (wd) {
bm_hal_wd_reload_counter(wd);
bm_hal_wd_log();
}
```

# Future Work

* Abstract additional hardware peripherals (GPIO, UART, SPI)

* Support more targets

* Add unit testing

* Currently, the API's mirror the ST hal. This is a good first step to break dependencies. But these apis' would likely need to change to accomodate other targets in the future.

* To truly port other targets, there would be more work thank just implementing the HAL api's

* for other ST chips: there would need to update src/CMakeLists.txt file to set linker files, memory layout, etc

* for other chips: would additionaly need to update compiler and tool chain

* ideally, this could all be specified in a configuration file

# Notes

* HAL implementations should avoid leaking vendor-specific types in the public API.

* All structures should be opaque to allow platform substitution.

# Contributing

When accessing hardware features (like watchdogs or GPIO), prefer using bm_hal_* APIs.

Avoid direct use of target HAL headers, such as the STM32 HAL or CMSIS, outside of HAL implementations.
39 changes: 39 additions & 0 deletions src/lib/bm_hal/bm_hal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* @file bm_hal.h
* @brief Bristlemouth Hardware Abstraction Layer.
*
* This header provides platform-specific hooks for low-level hardware functions
* that may be required by the system. Currently, only basic and limited interface
* is provided for a watchdog. But, in time, this may be expanded to include GPIO,
* UART, and other hardware interfaces as needed.
*
*/

#ifndef BM_HAL_H
#define BM_HAL_H

#ifdef __cplusplus
extern "C" {
#endif

// ====================================================
// @name Watchdog API
// ====================================================

/** @brief Opaque handle for a hardware watchdog instance. */
typedef struct bm_hal_wd* bm_hal_wd_t;

/** @brief Returns a handle to the platform’s watchdog instance. */
bm_hal_wd_t bm_hal_wd_get_handle(void);

/** @brief Reloads the hardware watchdog timer to prevent system reset. */
void bm_hal_wd_reload_counter(bm_hal_wd_t wd);

/** @brief Logs watchdog-related debug info (platform-specific). */
void bm_hal_wd_log(void);

#ifdef __cplusplus
}
#endif

#endif // BM_HAL_H
71 changes: 71 additions & 0 deletions src/lib/bm_hal/bm_hal_stm32u5xx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* @file bm_hal_stm32h5xxx.c
* @brief STM32H5xx-specific implementation of Bristlemouth HAL functions.
*
* Provides hardware-specific implementations of watchdog-related hooks used
* across the system, including the IWDG reload function and fault logging.
*/

// -----------------------------------------------------------------------------
// Includes
// -----------------------------------------------------------------------------

// Bristlemouth Includes
#include "bm_hal.h"

// FreeRTOS Includes
#include "FreeRTOS.h"

// STM32 Includes
#include "iwdg.h"

// ====================================================
// Watchdog API Implementations
// ====================================================

/**
* @brief Opaque watchdog handle used by bm_hal API.
*
* This struct wraps the platform-specific IWDG_TypeDef* and enables type-safe
* abstraction of the watchdog instance. It hides implementation details from
* users and allows for future extensibility without breaking the API.
*/
struct bm_hal_wd {
IWDG_TypeDef* iwdg;
};

static struct bm_hal_wd stm32_hw_wd = {
.iwdg = IWDG
};

bm_hal_wd_t bm_hal_wd_get_handle(void) {
return &stm32_hw_wd;
}

void bm_hal_wd_reload_counter(bm_hal_wd_t wd) {
if (wd && wd->iwdg) {
LL_IWDG_ReloadCounter(wd->iwdg);
}
}

// Optional log stub for watchdog diagnostics (e.g., Memfault)
void bm_hal_wd_log(void) {
// memfault_software_watchdog_feed();
}

// -----------------------------------------------------------------------------
// Watchdog IWDG IRQ Handler
// -----------------------------------------------------------------------------

// NOTE: This IRQ handler reconfigures and reloads the IWDG on interrupt.
// It assumes use of the STM32 LL (Low-Layer) APIs.
void IWDG_IRQHandler(void) {
LL_IWDG_EnableWriteAccess(IWDG);
while (LL_IWDG_IsReady(IWDG) != 1) {
// Wait until the IWDG is ready to accept configuration
}
LL_IWDG_SetPrescaler(IWDG, LL_IWDG_PRESCALER_1024);
LL_IWDG_ReloadCounter(IWDG);

MEMFAULT_ASSERT_EXTRA_AND_REASON(0, kMfltRebootReason_HardwareWatchdog);
}
Loading
Loading