|
| 1 | +# wolfHAL Integration |
| 2 | + |
| 3 | +wolfBoot supports [wolfHAL](https://github.com/wolfSSL/wolfHAL) as an alternative |
| 4 | +hardware abstraction layer backend. wolfHAL provides portable drivers for common MCU |
| 5 | +peripherals (clock, flash, GPIO, UART, SPI, etc.) with a consistent API across |
| 6 | +platforms. |
| 7 | + |
| 8 | +## Overview |
| 9 | + |
| 10 | +The wolfHAL integration uses a single generic `TARGET=wolfhal` with a per-board |
| 11 | +abstraction layer. All board-specific details — device instances, driver bindings, |
| 12 | +build flags, and linker scripts — live in a self-contained board directory. Adding |
| 13 | +support for a new board or MCU family requires no changes to the core build system or |
| 14 | +HAL shim. |
| 15 | + |
| 16 | +The integration uses wolfHAL's **direct API mapping** feature. Each platform |
| 17 | +driver source provides an optional `#ifdef` block that renames its driver |
| 18 | +functions to the top-level API names. When the corresponding |
| 19 | +`WHAL_CFG_<TYPE>_API_MAPPING_<VARIANT>` flag is defined, the driver file |
| 20 | +itself provides the definition of the top-level API — no wrapper, no vtable |
| 21 | +indirection, no runtime null-check. Calling `whal_Flash_Write(&dev, ...)` links |
| 22 | +directly to the platform driver's implementation. |
| 23 | + |
| 24 | +The integration consists of four parts: |
| 25 | + |
| 26 | +1. **Generic HAL shim** (`hal/wolfhal.c`) — implements the wolfBoot HAL API |
| 27 | + (`hal_flash_write`, `hal_flash_erase`, etc.) by calling the top-level wolfHAL |
| 28 | + API (`whal_Flash_Write`, `whal_Uart_Send`, etc.). This file is shared across |
| 29 | + all wolfHAL boards. |
| 30 | + |
| 31 | +2. **Board directory** (`hal/boards/<board>/`) — contains three files that fully |
| 32 | + describe a board: |
| 33 | + - `board.h` — includes the chip-specific wolfHAL driver headers and defines |
| 34 | + any board-level pin/peripheral enums. |
| 35 | + - `board.c` — device instances (clock, flash, GPIO, UART), configuration |
| 36 | + structs, and `hal_init`/`hal_prepare_boot` implementations. |
| 37 | + - `board.mk` — build variables (`ARCH_FLASH_OFFSET`, `LSCRIPT_IN`, the |
| 38 | + `WHAL_CFG_*_API_MAPPING_*` flags, wolfHAL driver objects, `RAM_CODE` |
| 39 | + linker rules). |
| 40 | + |
| 41 | +3. **Generic test application** (`test-app/app_wolfhal.c`) — demonstrates using |
| 42 | + wolfHAL peripherals (GPIO, UART) beyond what the bootloader needs, using the |
| 43 | + same top-level wolfHAL API. |
| 44 | + |
| 45 | +4. **wolfHAL library** (`lib/wolfHAL/`) — the wolfHAL submodule containing the |
| 46 | + platform drivers. |
| 47 | + |
| 48 | +### How It Fits Together |
| 49 | + |
| 50 | +``` |
| 51 | +config/examples/wolfhal_<board>.config |
| 52 | + └─ TARGET=wolfhal BOARD=<board> |
| 53 | +
|
| 54 | +arch.mk |
| 55 | + └─ Sets WOLFHAL_ROOT, CFLAGS += -Ihal/boards/$(BOARD) |
| 56 | +
|
| 57 | +Makefile |
| 58 | + └─ OBJS += hal/boards/$(BOARD)/board.o |
| 59 | + └─ include hal/boards/$(BOARD)/board.mk |
| 60 | +
|
| 61 | +hal/wolfhal.c (generic — calls whal_Flash_Write, whal_Uart_Send, etc.) |
| 62 | + └─ #include "board.h" (resolved via -I to the board directory) |
| 63 | +
|
| 64 | +hal/boards/<board>/ |
| 65 | + ├─ board.h (includes wolfHAL driver headers, pin enums) |
| 66 | + ├─ board.c (device instances, hal_init, hal_prepare_boot) |
| 67 | + └─ board.mk (WHAL_CFG_*_API_MAPPING_*, driver objects, RAM_CODE rules) |
| 68 | +``` |
| 69 | + |
| 70 | +The `WHAL_CFG_*_API_MAPPING_*` flags cause each wolfHAL driver source to emit |
| 71 | +its functions under the top-level API name. Since only one driver source per |
| 72 | +device type is compiled, there is no conflict — and since no dispatch source |
| 73 | +(e.g., `src/flash/flash.c`) is included, there is no vtable indirection. The |
| 74 | +linker can garbage-collect any unused symbols with `-Wl,--gc-sections`. |
| 75 | + |
| 76 | +## Configuration |
| 77 | + |
| 78 | +A wolfHAL-based config requires two variables beyond the standard wolfBoot settings: |
| 79 | + |
| 80 | +``` |
| 81 | +TARGET=wolfhal |
| 82 | +BOARD=stm32wb_nucleo |
| 83 | +``` |
| 84 | + |
| 85 | +- `TARGET=wolfhal` selects the generic wolfHAL HAL shim and build path. |
| 86 | +- `BOARD` selects the board directory under `hal/boards/`. |
| 87 | + |
| 88 | +See `config/examples/wolfhal_*.config` for complete examples. |
| 89 | + |
| 90 | +## Adding a New Board |
| 91 | + |
| 92 | +To add a new board, create a directory `hal/boards/<board_name>/` with three files: |
| 93 | + |
| 94 | +### 1. `board.h` — Driver Headers and Pin Enums |
| 95 | + |
| 96 | +Include the chip-specific wolfHAL driver headers and declare any board-level |
| 97 | +enums (pin indices, peripheral identifiers): |
| 98 | + |
| 99 | +```c |
| 100 | +#ifndef WOLFHAL_BOARD_H |
| 101 | +#define WOLFHAL_BOARD_H |
| 102 | + |
| 103 | +#include <wolfHAL/clock/<family>_rcc.h> |
| 104 | +#include <wolfHAL/flash/<family>_flash.h> |
| 105 | +#include <wolfHAL/gpio/<family>_gpio.h> |
| 106 | +#include <wolfHAL/uart/<family>_uart.h> |
| 107 | + |
| 108 | +/* GPIO pin indices (matches pin array in board.c) */ |
| 109 | +enum { |
| 110 | + BOARD_LED_PIN, |
| 111 | + BOARD_UART_TX_PIN, |
| 112 | + BOARD_UART_RX_PIN, |
| 113 | + BOARD_PIN_COUNT, |
| 114 | +}; |
| 115 | + |
| 116 | +#endif /* WOLFHAL_BOARD_H */ |
| 117 | +``` |
| 118 | + |
| 119 | +### 2. `board.c` — Device Instances and Initialization |
| 120 | + |
| 121 | +Define the wolfHAL device instances and implement `hal_init` and `hal_prepare_boot` |
| 122 | +using the top-level wolfHAL API. The file must export `g_wbFlash` (and `g_wbUart` |
| 123 | +when `DEBUG_UART` is enabled) as non-static globals — these are referenced by |
| 124 | +`hal/wolfhal.c` via `extern`. |
| 125 | + |
| 126 | +```c |
| 127 | +#include "hal.h" |
| 128 | +#include "board.h" |
| 129 | + |
| 130 | +/* Clock controller */ |
| 131 | +whal_Clock g_wbClock = { |
| 132 | + .regmap = { .base = ..., .size = 0x400 }, |
| 133 | + .cfg = &(whal_<Family>Rcc_Cfg) { ... }, |
| 134 | +}; |
| 135 | + |
| 136 | +/* Flash */ |
| 137 | +whal_Flash g_wbFlash = { |
| 138 | + .regmap = { .base = ..., .size = 0x400 }, |
| 139 | + .cfg = &(whal_<Family>Flash_Cfg) { |
| 140 | + .startAddr = 0x08000000, |
| 141 | + .size = ..., |
| 142 | + }, |
| 143 | +}; |
| 144 | + |
| 145 | +#ifdef DEBUG_UART |
| 146 | +whal_Gpio g_wbGpio = { ... }; |
| 147 | +whal_Uart g_wbUart = { ... }; |
| 148 | +#endif |
| 149 | + |
| 150 | +void hal_init(void) |
| 151 | +{ |
| 152 | + whal_Clock_Init(&g_wbClock); |
| 153 | + whal_Flash_Init(&g_wbFlash); |
| 154 | +#ifdef DEBUG_UART |
| 155 | + whal_Gpio_Init(&g_wbGpio); |
| 156 | + whal_Uart_Init(&g_wbUart); |
| 157 | +#endif |
| 158 | +} |
| 159 | + |
| 160 | +void hal_prepare_boot(void) |
| 161 | +{ |
| 162 | +#ifdef DEBUG_UART |
| 163 | + whal_Uart_Deinit(&g_wbUart); |
| 164 | + whal_Gpio_Deinit(&g_wbGpio); |
| 165 | +#endif |
| 166 | + whal_Flash_Deinit(&g_wbFlash); |
| 167 | + whal_Clock_Deinit(&g_wbClock); |
| 168 | +} |
| 169 | +``` |
| 170 | +
|
| 171 | +Note: the device instance's `.driver` field is intentionally left unset. With |
| 172 | +API mapping, the top-level `whal_*_Init`/etc. symbols are the driver functions |
| 173 | +themselves — there is no dispatch through a vtable. |
| 174 | +
|
| 175 | +### 3. `board.mk` — Build Variables |
| 176 | +
|
| 177 | +Provide the build-time configuration: API mapping flags, flash offset, linker |
| 178 | +script, and the wolfHAL driver objects needed for your MCU family. **Do not |
| 179 | +compile the dispatch source (`src/<type>/<type>.c`)** — it would provide a |
| 180 | +duplicate definition of the top-level API symbols. |
| 181 | +
|
| 182 | +```makefile |
| 183 | +ARCH_FLASH_OFFSET=0x08000000 |
| 184 | +LSCRIPT_IN=hal/<family>.ld |
| 185 | +
|
| 186 | +# Bind wolfHAL driver sources directly to the top-level API symbols. |
| 187 | +CFLAGS+=-DWHAL_CFG_CLOCK_API_MAPPING_<FAMILY> |
| 188 | +CFLAGS+=-DWHAL_CFG_FLASH_API_MAPPING_<FAMILY> |
| 189 | +CFLAGS+=-DWHAL_CFG_GPIO_API_MAPPING_<FAMILY> |
| 190 | +CFLAGS+=-DWHAL_CFG_UART_API_MAPPING_<FAMILY> |
| 191 | +
|
| 192 | +WOLFHAL_OBJS+=$(WOLFHAL_ROOT)/src/clock/<family>_rcc.o |
| 193 | +WOLFHAL_OBJS+=$(WOLFHAL_ROOT)/src/flash/<family>_flash.o |
| 194 | +ifeq ($(DEBUG_UART),1) |
| 195 | + WOLFHAL_OBJS+=$(WOLFHAL_ROOT)/src/gpio/<family>_gpio.o |
| 196 | + WOLFHAL_OBJS+=$(WOLFHAL_ROOT)/src/uart/<family>_uart.o |
| 197 | +endif |
| 198 | +
|
| 199 | +OBJS+=$(WOLFHAL_OBJS) |
| 200 | +APP_OBJS+=$(WOLFHAL_OBJS) |
| 201 | +
|
| 202 | +ifeq ($(RAM_CODE),1) |
| 203 | + WOLFHAL_FLASH_EXCLUDE_TEXT=*(EXCLUDE_FILE(*<family>_flash.o) .text*) |
| 204 | + WOLFHAL_FLASH_EXCLUDE_RODATA=*(EXCLUDE_FILE(*<family>_flash.o) .rodata*) |
| 205 | + WOLFHAL_FLASH_RAM_SECTIONS=*<family>_flash.o(.text* .rodata*) |
| 206 | +endif |
| 207 | +``` |
| 208 | + |
| 209 | +Only one API mapping flag may be active per device type per build. |
| 210 | + |
| 211 | +### 4. Config File |
| 212 | + |
| 213 | +Create `config/examples/wolfhal_<board_name>.config`: |
| 214 | + |
| 215 | +``` |
| 216 | +TARGET=wolfhal |
| 217 | +BOARD=<board_name> |
| 218 | +SIGN=ECC256 |
| 219 | +HASH=SHA256 |
| 220 | +WOLFBOOT_SECTOR_SIZE=0x1000 |
| 221 | +WOLFBOOT_PARTITION_SIZE=0x20000 |
| 222 | +WOLFBOOT_PARTITION_BOOT_ADDRESS=0x08008000 |
| 223 | +WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x08028000 |
| 224 | +WOLFBOOT_PARTITION_SWAP_ADDRESS=0x08048000 |
| 225 | +NVM_FLASH_WRITEONCE=1 |
| 226 | +``` |
| 227 | + |
| 228 | +Adjust partition addresses and sector sizes for your board's flash layout. Optionally |
| 229 | +add `DEBUG_UART=1` to enable UART debug output. |
| 230 | + |
| 231 | +## RAM_CODE |
| 232 | + |
| 233 | +When `RAM_CODE=1` is set, wolfBoot's core flash update functions are placed in RAM |
| 234 | +via the `RAMFUNCTION` attribute. For wolfHAL boards, the `board.mk` defines |
| 235 | +`EXCLUDE_FILE` rules that also place the wolfHAL flash driver into RAM. This ensures |
| 236 | +all flash operations execute from RAM, which is required on MCUs that stall or fault |
| 237 | +when code executes from the same flash bank being programmed. |
| 238 | + |
| 239 | +The linker script uses `@WOLFHAL_FLASH_EXCLUDE_TEXT@`, |
| 240 | +`@WOLFHAL_FLASH_EXCLUDE_RODATA@`, and `@WOLFHAL_FLASH_RAM_SECTIONS@` placeholders |
| 241 | +that are substituted at build time. When `RAM_CODE=1`, these expand to |
| 242 | +`EXCLUDE_FILE` rules that move the flash driver's `.text` and `.rodata` sections from |
| 243 | +flash into the `.data` section (loaded to RAM at startup). When `RAM_CODE` is not |
| 244 | +set, all code remains in flash as normal. |
| 245 | + |
| 246 | +## Test Application |
| 247 | + |
| 248 | +The generic test application (`test-app/app_wolfhal.c`) demonstrates using wolfHAL |
| 249 | +peripherals beyond what the bootloader needs. It accesses the board-provided GPIO and |
| 250 | +UART instances (`g_wbGpio`, `g_wbUart`) via `extern`, using the top-level wolfHAL |
| 251 | +API (`whal_Gpio_Set`, `whal_Uart_Send`) to toggle an LED and send serial output, |
| 252 | +then exercises the wolfBoot update mechanism. |
| 253 | + |
| 254 | +The test-app Makefile compiles its own copy of the board file (`board_<board>.o`) |
| 255 | +with `DEBUG_UART=1` always defined, since the app needs UART and GPIO regardless of |
| 256 | +the bootloader's `DEBUG_UART` setting. |
0 commit comments