Skip to content

Latest commit

 

History

History
250 lines (194 loc) · 8.19 KB

File metadata and controls

250 lines (194 loc) · 8.19 KB

wolfHAL Integration

wolfBoot supports wolfHAL as an alternative hardware abstraction layer backend. wolfHAL provides portable drivers for common MCU peripherals (clock, flash, GPIO, UART, SPI, etc.) with a consistent API across platforms.

Overview

The wolfHAL integration uses a single generic TARGET=wolfhal with a per-board abstraction layer. All board-specific details — device instances, driver bindings, build flags, and linker scripts — live in a self-contained board directory. Adding support for a new board or MCU family requires no changes to the core build system or HAL shim.

The integration consists of four parts:

  1. Generic HAL shim (hal/wolfhal.c) — implements the wolfBoot HAL API (hal_flash_write, hal_flash_erase, etc.) by calling Board_* macros. This file is shared across all wolfHAL boards.

  2. Board directory (hal/boards/<board>/) — contains three files that fully describe a board:

    • board.h#define macros mapping generic Board_* APIs to chip-specific wolfHAL driver functions.
    • board.c — device instances (clock, flash, GPIO, UART), configuration structs, and hal_init/hal_prepare_boot implementations.
    • board.mk — build variables (ARCH_FLASH_OFFSET, LSCRIPT_IN, wolfHAL driver objects, RAM_CODE linker rules).
  3. Generic test application (test-app/app_wolfhal.c) — demonstrates using wolfHAL peripherals (GPIO, UART) beyond what the bootloader needs, using the same Board_* API.

  4. wolfHAL library (lib/wolfHAL/) — the wolfHAL submodule containing the platform drivers.

How It Fits Together

config/examples/wolfhal_<board>.config
  └─ TARGET=wolfhal  BOARD=<board>

arch.mk
  └─ Sets WOLFHAL_ROOT, CFLAGS += -Ihal/boards/$(BOARD)

Makefile
  └─ OBJS += hal/boards/$(BOARD)/board.o
  └─ include hal/boards/$(BOARD)/board.mk

hal/wolfhal.c  (generic — calls Board_Flash_Write, Board_Uart_Send, etc.)
  └─ #include "board.h"  (resolved via -I to the board directory)

hal/boards/<board>/
  ├─ board.h   (#define Board_Flash_Write → whal_<family>Flash_Write)
  ├─ board.c   (device instances, hal_init, hal_prepare_boot)
  └─ board.mk  (ARCH_FLASH_OFFSET, LSCRIPT_IN, driver objects, RAM_CODE rules)

The board.h macros resolve Board_* calls directly to the chip-specific wolfHAL driver functions at compile time. This avoids vtable indirection and allows the linker to garbage-collect unused driver code (with -Wl,--gc-sections).

Configuration

A wolfHAL-based config requires two variables beyond the standard wolfBoot settings:

TARGET=wolfhal
BOARD=stm32wb_nucleo
  • TARGET=wolfhal selects the generic wolfHAL HAL shim and build path.
  • BOARD selects the board directory under hal/boards/.

See config/examples/wolfhal_*.config for complete examples.

Adding a New Board

To add a new board, create a directory hal/boards/<board_name>/ with three files:

1. board.h — API Mappings

Map each generic Board_* macro to the appropriate wolfHAL driver function for your MCU family. The required mappings are:

#ifndef WOLFHAL_BOARD_H
#define WOLFHAL_BOARD_H

#include <wolfHAL/clock/<family>_clock.h>
#include <wolfHAL/flash/<family>_flash.h>
#include <wolfHAL/gpio/<family>_gpio.h>
#include <wolfHAL/uart/<family>_uart.h>

/* Clock */
#define Board_Clock_Init     whal_<Family>Clock_Init
#define Board_Clock_Deinit   whal_<Family>Clock_Deinit
#define Board_Clock_Enable   whal_<Family>Clock_Enable
#define Board_Clock_Disable  whal_<Family>Clock_Disable

/* Flash */
#define Board_Flash_Init     whal_<Family>Flash_Init
#define Board_Flash_Deinit   whal_<Family>Flash_Deinit
#define Board_Flash_Lock     whal_<Family>Flash_Lock
#define Board_Flash_Unlock   whal_<Family>Flash_Unlock
#define Board_Flash_Write    whal_<Family>Flash_Write
#define Board_Flash_Erase    whal_<Family>Flash_Erase

/* GPIO */
#define Board_Gpio_Init      whal_<Family>Gpio_Init
#define Board_Gpio_Deinit    whal_<Family>Gpio_Deinit
#define Board_Gpio_Set       whal_<Family>Gpio_Set
#define Board_Gpio_Get       whal_<Family>Gpio_Get

/* UART */
#define Board_Uart_Init      whal_<Family>Uart_Init
#define Board_Uart_Deinit    whal_<Family>Uart_Deinit
#define Board_Uart_Send      whal_<Family>Uart_Send
#define Board_Uart_Recv      whal_<Family>Uart_Recv

#endif /* WOLFHAL_BOARD_H */

2. board.c — Device Instances and Initialization

Define the wolfHAL device instances and implement hal_init and hal_prepare_boot. The file must export g_wbFlash (and g_wbUart when DEBUG_UART is enabled) as non-static globals — these are referenced by hal/wolfhal.c via extern.

#include "hal.h"
#include "board.h"

/* Clock controller */
whal_Clock g_wbClock = {
    .regmap = { .base = ..., .size = 0x400 },
    .cfg = &(<family>_clock_cfg) { ... },
};

/* Flash */
whal_Flash g_wbFlash = {
    .regmap = { .base = ..., .size = 0x400 },
    .cfg = &(<family>_flash_cfg) {
        .startAddr = 0x08000000,
        .size = ...,
    },
};

#ifdef DEBUG_UART
whal_Gpio g_wbGpio = { ... };
whal_Uart g_wbUart = { ... };
#endif

void hal_init(void)
{
    /* Initialize clock tree, flash, and optionally GPIO/UART */
    Board_Clock_Init(&g_wbClock);
    Board_Flash_Init(&g_wbFlash);
#ifdef DEBUG_UART
    Board_Gpio_Init(&g_wbGpio);
    Board_Uart_Init(&g_wbUart);
#endif
}

void hal_prepare_boot(void)
{
#ifdef DEBUG_UART
    Board_Uart_Deinit(&g_wbUart);
    Board_Gpio_Deinit(&g_wbGpio);
#endif
    Board_Flash_Deinit(&g_wbFlash);
    Board_Clock_Deinit(&g_wbClock);
}

3. board.mk — Build Variables

Provide the build-time configuration: flash offset, linker script, and the wolfHAL driver objects needed for your MCU family.

ARCH_FLASH_OFFSET=0x08000000
LSCRIPT_IN=hal/<family>.ld

WOLFHAL_OBJS+=$(WOLFHAL_ROOT)/src/clock/<family>_clock.o
WOLFHAL_OBJS+=$(WOLFHAL_ROOT)/src/flash/<family>_flash.o
ifeq ($(DEBUG_UART),1)
  WOLFHAL_OBJS+=$(WOLFHAL_ROOT)/src/gpio/<family>_gpio.o
  WOLFHAL_OBJS+=$(WOLFHAL_ROOT)/src/uart/<family>_uart.o
endif

OBJS+=$(WOLFHAL_OBJS)
APP_OBJS+=$(WOLFHAL_OBJS)

ifeq ($(RAM_CODE),1)
  WOLFHAL_FLASH_EXCLUDE_TEXT=*(EXCLUDE_FILE(*<family>_flash.o) .text*)
  WOLFHAL_FLASH_EXCLUDE_RODATA=*(EXCLUDE_FILE(*<family>_flash.o) .rodata*)
  WOLFHAL_FLASH_RAM_SECTIONS=*<family>_flash.o(.text* .rodata*)
endif

4. Config File

Create config/examples/wolfhal_<board_name>.config:

TARGET=wolfhal
BOARD=<board_name>
SIGN=ECC256
HASH=SHA256
WOLFBOOT_SECTOR_SIZE=0x1000
WOLFBOOT_PARTITION_SIZE=0x20000
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x08008000
WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x08028000
WOLFBOOT_PARTITION_SWAP_ADDRESS=0x08048000
NVM_FLASH_WRITEONCE=1

Adjust partition addresses and sector sizes for your board's flash layout. Optionally add DEBUG_UART=1 to enable UART debug output.

RAM_CODE

When RAM_CODE=1 is set, wolfBoot's core flash update functions are placed in RAM via the RAMFUNCTION attribute. For wolfHAL boards, the board.mk defines EXCLUDE_FILE rules that also place the wolfHAL flash driver into RAM. This ensures all flash operations execute from RAM, which is required on MCUs that stall or fault when code executes from the same flash bank being programmed.

The linker script uses @WOLFHAL_FLASH_EXCLUDE_TEXT@, @WOLFHAL_FLASH_EXCLUDE_RODATA@, and @WOLFHAL_FLASH_RAM_SECTIONS@ placeholders that are substituted at build time. When RAM_CODE=1, these expand to EXCLUDE_FILE rules that move the flash driver's .text and .rodata sections from flash into the .data section (loaded to RAM at startup). When RAM_CODE is not set, all code remains in flash as normal.

Test Application

The generic test application (test-app/app_wolfhal.c) demonstrates using wolfHAL peripherals beyond what the bootloader needs. It initializes GPIO and UART via the Board_* API, then exercises the wolfBoot update mechanism.

The test app re-uses the board's clock instance via extern whal_Clock g_wbClock to enable peripheral clocks for its own devices (e.g. GPIO for an LED, UART for serial output).

The test-app Makefile compiles its own copy of the board file (board_<board>.o) with DEBUG_UART=1 always defined, since the app needs UART and GPIO regardless of the bootloader's DEBUG_UART setting.