Skip to content
Merged
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
25 changes: 12 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,32 @@ on:
jobs:
core-tests:
runs-on: ubuntu-latest
strategy:
matrix:
extra_cflags: ["", "-DWHAL_CFG_NO_TIMEOUT"]
steps:
- uses: actions/checkout@v4

- name: Build and run core tests
working-directory: tests/core
run: make run
run: CFLAGS="${{ matrix.extra_cflags }}" make run

cross-compile:
runs-on: ubuntu-latest
strategy:
matrix:
board: [stm32wb55xx_nucleo, pic32cz_curiosity_ultra]
extra_cflags: ["", "-DWHAL_CFG_NO_TIMEOUT"]
steps:
- uses: actions/checkout@v4

- name: Install ARM toolchain
run: sudo apt-get update && sudo apt-get install -y gcc-arm-none-eabi

- name: Build blinky (STM32WB)
- name: Build blinky
working-directory: examples/blinky
run: make BOARD=stm32wb55xx_nucleo
run: CFLAGS="${{ matrix.extra_cflags }}" make BOARD=${{ matrix.board }}

- name: Build blinky (PIC32CZ)
working-directory: examples/blinky
run: make BOARD=pic32cz_curiosity_ultra

- name: Build tests (STM32WB)
working-directory: tests
run: make BOARD=stm32wb55xx_nucleo

- name: Build tests (PIC32CZ)
- name: Build tests
working-directory: tests
run: make BOARD=pic32cz_curiosity_ultra
run: CFLAGS="${{ matrix.extra_cflags }}" make BOARD=${{ matrix.board }}
10 changes: 5 additions & 5 deletions boards/pic32cz_curiosity_ultra/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,13 @@ whal_Flash g_whalFlash = {
};

/* SysTick timing */
volatile size_t g_tick = 0;
volatile uint32_t g_tick = 0;
volatile uint8_t g_waiting = 0;
volatile uint8_t g_tickOverflow = 0;

void SysTick_Handler()
{
size_t tickBefore = g_tick++;
uint32_t tickBefore = g_tick++;
if (g_waiting) {
if (tickBefore > g_tick)
g_tickOverflow = 1;
Expand All @@ -130,12 +130,12 @@ void SysTick_Handler()

void Board_WaitMs(size_t ms)
{
size_t startCount = g_tick;
uint32_t startCount = g_tick;
g_waiting = 1;
while (1) {
size_t currentCount = g_tick;
uint32_t currentCount = g_tick;
if (g_tickOverflow) {
if ((SIZE_MAX - startCount) + currentCount > ms) {
if ((UINT32_MAX - startCount) + currentCount > ms) {
break;
}
} else if (currentCount - startCount > ms) {
Expand Down
2 changes: 1 addition & 1 deletion boards/pic32cz_curiosity_ultra/board.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extern whal_Timer g_whalTimer;
extern whal_Uart g_whalUart;
extern whal_Flash g_whalFlash;

extern volatile size_t g_tick;
extern volatile uint32_t g_tick;

#define BOARD_LED_PIN 0
#define BOARD_FLASH_TEST_ADDR 0x0C000000
Expand Down
48 changes: 31 additions & 17 deletions boards/stm32wb55xx_nucleo/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@
#include "board.h"
#include <wolfHAL/platform/st/stm32wb55xx.h>

/* SysTick timing */
volatile uint32_t g_tick = 0;
volatile uint8_t g_waiting = 0;
volatile uint8_t g_tickOverflow = 0;

void SysTick_Handler()
{
uint32_t tickBefore = g_tick++;
if (g_waiting) {
if (tickBefore > g_tick)
g_tickOverflow = 1;
}
}

uint32_t Board_GetTick(void)
{
return g_tick;
}

whal_Timeout g_whalTimeout = {
.timeoutTicks = 5, /* 5ms timeout */
.GetTick = Board_GetTick,
};

/* Clock */
whal_Clock g_whalClock = {
WHAL_STM32WB55_RCC_PLL_DEVICE,
Expand Down Expand Up @@ -95,6 +119,7 @@ whal_Uart g_whalUart = {
.cfg = &(whal_Stm32wbUart_Cfg) {
.clkCtrl = &g_whalClock,
.clk = &(whal_Stm32wbRcc_Clk) {WHAL_STM32WB55_UART1_CLOCK},
.timeout = &g_whalTimeout,

.baud = 115200,
},
Expand All @@ -107,6 +132,7 @@ whal_Flash g_whalFlash = {
.cfg = &(whal_Stm32wbFlash_Cfg) {
.clkCtrl = &g_whalClock,
.clk = &(whal_Stm32wbRcc_Clk) {WHAL_STM32WB55_FLASH_CLOCK},
.timeout = &g_whalTimeout,

.startAddr = 0x08000000,
.size = 0x100000,
Expand All @@ -120,6 +146,7 @@ whal_Rng g_whalRng = {
.cfg = &(whal_Stm32wbRng_Cfg) {
.clkCtrl = &g_whalClock,
.clk = &(whal_Stm32wbRcc_Clk) {WHAL_STM32WB55_RNG_CLOCK},
.timeout = &g_whalTimeout,
},
};

Expand All @@ -142,31 +169,18 @@ whal_Crypto g_whalCrypto = {
.cfg = &(whal_Stm32wbAes_Cfg) {
.clkCtrl = &g_whalClock,
.clk = &(whal_Stm32wbRcc_Clk) {WHAL_STM32WB55_AES1_CLOCK},
.timeout = &g_whalTimeout,
},
};

/* SysTick timing */
volatile size_t g_tick = 0;
volatile uint8_t g_waiting = 0;
volatile uint8_t g_tickOverflow = 0;

void SysTick_Handler()
{
size_t tickBefore = g_tick++;
if (g_waiting) {
if (tickBefore > g_tick)
g_tickOverflow = 1;
}
}

void Board_WaitMs(size_t ms)
{
size_t startCount = g_tick;
uint32_t startCount = g_tick;
g_waiting = 1;
while (1) {
size_t currentCount = g_tick;
uint32_t currentCount = g_tick;
if (g_tickOverflow) {
if ((SIZE_MAX - startCount) + currentCount > ms) {
if ((UINT32_MAX - startCount) + currentCount > ms) {
break;
}
} else if (currentCount - startCount > ms) {
Expand Down
2 changes: 1 addition & 1 deletion boards/stm32wb55xx_nucleo/board.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ extern whal_Flash g_whalFlash;
extern whal_Rng g_whalRng;
extern whal_Crypto g_whalCrypto;

extern volatile size_t g_tick;
extern volatile uint32_t g_tick;

#define BOARD_LED_PIN 0
#define BOARD_FLASH_TEST_ADDR 0x08080000
Expand Down
11 changes: 6 additions & 5 deletions docs/adding_a_board.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ Exports global peripheral instances and board-specific constants:

#include <wolfHAL/wolfHAL.h>

extern whal_Clock g_whalClock;
extern whal_Gpio g_whalGpio;
extern whal_Uart g_whalUart;
extern whal_Timer g_whalTimer;
extern whal_Flash g_whalFlash;
extern whal_Clock g_whalClock;
extern whal_Gpio g_whalGpio;
extern whal_Uart g_whalUart;
extern whal_Timer g_whalTimer;
extern whal_Flash g_whalFlash;
extern whal_Timeout g_whalTimeout;

#define BOARD_LED_PIN 0

Expand Down
2 changes: 2 additions & 0 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ whal_Uart g_whalUart = {
.cfg = &(whal_Stm32wbUart_Cfg) {
.clkCtrl = &g_whalClock,
.clk = &(whal_Stm32wbRcc_Clk){WHAL_STM32WB55_UART1_CLOCK},
.timeout = &g_whalTimeout,
.baud = 115200,
},
};
Expand Down Expand Up @@ -202,6 +203,7 @@ operation completed. The error codes are:
| `WHAL_EINVAL` | Invalid argument or unsupported operation |
| `WHAL_ENOTREADY` | Resource is busy or not yet available |
| `WHAL_EHARDWARE` | Hardware error (e.g., RNG entropy failure) |
| `WHAL_ETIMEOUT` | Operation timed out waiting for hardware |

## Optimizing for Size

Expand Down
118 changes: 118 additions & 0 deletions docs/writing_a_driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,124 @@ Use `Write` and `Read` for whole-register access. Use `Update` and `Get` when
you need to modify or read individual fields within a register without
disturbing other bits.

### Timeouts

Most drivers poll hardware status registers in busy-wait loops (e.g., waiting
for a DMA transfer to complete, a flash erase to finish, or an AES computation
to produce a result). wolfHAL provides an optional timeout mechanism to bound
these waits and prevent infinite hangs if hardware misbehaves.

#### Timeout Struct

The board creates a `whal_Timeout` instance with a tick source and a timeout
duration. Drivers receive a pointer to it through their configuration struct:

```c
typedef struct {
uint32_t timeoutTicks; /* max ticks before timeout */
uint32_t startTick; /* snapshot, set by START */
uint32_t (*GetTick)(void); /* board-provided tick source */
} whal_Timeout;
```

The tick units are determined by the board's `GetTick` implementation. A 1 kHz
SysTick gives millisecond ticks; a 1 MHz timer gives microsecond ticks. Drivers
do not need to know the tick rate.

#### whal_Reg_ReadPoll

For the common case of polling a register bit, use `whal_Reg_ReadPoll` from
`wolfHAL/regmap.h`:

```c
whal_Error whal_Reg_ReadPoll(size_t base, size_t offset,
size_t mask, size_t value,
whal_Timeout *timeout);
```

This polls until `(reg & mask) == value` or the timeout expires. Pass the mask
as the value to wait for bits to be set, or `0` to wait for bits to be clear:

```c
/* Wait for TXE flag to be set */
err = whal_Reg_ReadPoll(base, SPI_SR_REG, SPI_SR_TXE_Msk,
SPI_SR_TXE_Msk, cfg->timeout);

/* Wait for BSY flag to be clear */
err = whal_Reg_ReadPoll(base, SPI_SR_REG, SPI_SR_BSY_Msk,
0, cfg->timeout);
```

A NULL timeout pointer means unbounded wait (poll forever).

#### Driver-Specific Helpers

When a driver has many polling sites that also need post-poll cleanup (e.g.,
clearing a flag), wrap the pattern in a local helper to avoid code duplication:

```c
static whal_Error WaitForCCF(size_t base, whal_Timeout *timeout)
{
whal_Error err;
err = whal_Reg_ReadPoll(base, AES_SR_REG, AES_SR_CCF_Msk,
AES_SR_CCF_Msk, timeout);
if (err)
return err;
whal_Reg_Update(base, AES_CR_REG, AES_CR_CCFC_Msk, AES_CR_CCFC_Msk);
return WHAL_SUCCESS;
}
```

This keeps code size small — one function body shared across all call sites
instead of inlined polling loops at each location.

#### Cleanup on Timeout

When a timeout occurs during an operation that has enabled a hardware mode
(e.g., flash programming mode, AES enable), the driver must still clean up
before returning. Use a `goto cleanup` pattern:

```c
whal_Error err = WHAL_SUCCESS;

whal_Reg_Update(base, CR_REG, PG_Msk, 1); /* enable programming mode */

for (...) {
err = whal_Reg_ReadPoll(base, SR_REG, BSY_Msk, 0, cfg->timeout);
if (err)
goto cleanup;
}

cleanup:
whal_Reg_Update(base, CR_REG, PG_Msk, 0); /* always disable */
return err;
```

Never return directly from inside a polling loop if the peripheral is in a
mode that requires cleanup.

#### Compile-Time Disable

Define `WHAL_CFG_NO_TIMEOUT` to remove all timeout logic from the binary.
When defined, `WHAL_TIMEOUT_START` becomes a no-op and `WHAL_TIMEOUT_EXPIRED`
always evaluates to `0`, so polling loops run until the hardware condition is
met with no overhead.

#### Adding Timeout to a Config Struct

Driver config structs that use polling should include an optional timeout
pointer:

```c
typedef struct {
/* ... other config fields ... */
whal_Timeout *timeout;
} whal_MyplatformFoo_Cfg;
```

The timeout is optional — if the board does not set it (NULL), all waits are
unbounded.

### Avoiding Bloat

When a peripheral has multiple distinct operating modes or configurations,
Expand Down
Loading
Loading