Skip to content

Commit 2c664ba

Browse files
authored
Merge pull request #16 from AlexLanzano/timeouts
[timeout, stm32wb, pic32cz] Implement an optional timeout and add it to current drivers
2 parents 74599d0 + 0498bb0 commit 2c664ba

29 files changed

Lines changed: 670 additions & 186 deletions

.github/workflows/ci.yml

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,32 @@ on:
99
jobs:
1010
core-tests:
1111
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
extra_cflags: ["", "-DWHAL_CFG_NO_TIMEOUT"]
1215
steps:
1316
- uses: actions/checkout@v4
1417

1518
- name: Build and run core tests
1619
working-directory: tests/core
17-
run: make run
20+
run: CFLAGS="${{ matrix.extra_cflags }}" make run
1821

1922
cross-compile:
2023
runs-on: ubuntu-latest
24+
strategy:
25+
matrix:
26+
board: [stm32wb55xx_nucleo, pic32cz_curiosity_ultra]
27+
extra_cflags: ["", "-DWHAL_CFG_NO_TIMEOUT"]
2128
steps:
2229
- uses: actions/checkout@v4
2330

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

27-
- name: Build blinky (STM32WB)
34+
- name: Build blinky
2835
working-directory: examples/blinky
29-
run: make BOARD=stm32wb55xx_nucleo
36+
run: CFLAGS="${{ matrix.extra_cflags }}" make BOARD=${{ matrix.board }}
3037

31-
- name: Build blinky (PIC32CZ)
32-
working-directory: examples/blinky
33-
run: make BOARD=pic32cz_curiosity_ultra
34-
35-
- name: Build tests (STM32WB)
36-
working-directory: tests
37-
run: make BOARD=stm32wb55xx_nucleo
38-
39-
- name: Build tests (PIC32CZ)
38+
- name: Build tests
4039
working-directory: tests
41-
run: make BOARD=pic32cz_curiosity_ultra
40+
run: CFLAGS="${{ matrix.extra_cflags }}" make BOARD=${{ matrix.board }}

boards/pic32cz_curiosity_ultra/board.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,13 @@ whal_Flash g_whalFlash = {
115115
};
116116

117117
/* SysTick timing */
118-
volatile size_t g_tick = 0;
118+
volatile uint32_t g_tick = 0;
119119
volatile uint8_t g_waiting = 0;
120120
volatile uint8_t g_tickOverflow = 0;
121121

122122
void SysTick_Handler()
123123
{
124-
size_t tickBefore = g_tick++;
124+
uint32_t tickBefore = g_tick++;
125125
if (g_waiting) {
126126
if (tickBefore > g_tick)
127127
g_tickOverflow = 1;
@@ -130,12 +130,12 @@ void SysTick_Handler()
130130

131131
void Board_WaitMs(size_t ms)
132132
{
133-
size_t startCount = g_tick;
133+
uint32_t startCount = g_tick;
134134
g_waiting = 1;
135135
while (1) {
136-
size_t currentCount = g_tick;
136+
uint32_t currentCount = g_tick;
137137
if (g_tickOverflow) {
138-
if ((SIZE_MAX - startCount) + currentCount > ms) {
138+
if ((UINT32_MAX - startCount) + currentCount > ms) {
139139
break;
140140
}
141141
} else if (currentCount - startCount > ms) {

boards/pic32cz_curiosity_ultra/board.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ extern whal_Timer g_whalTimer;
1111
extern whal_Uart g_whalUart;
1212
extern whal_Flash g_whalFlash;
1313

14-
extern volatile size_t g_tick;
14+
extern volatile uint32_t g_tick;
1515

1616
#define BOARD_LED_PIN 0
1717
#define BOARD_FLASH_TEST_ADDR 0x0C000000

boards/stm32wb55xx_nucleo/board.c

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,30 @@
55
#include "board.h"
66
#include <wolfHAL/platform/st/stm32wb55xx.h>
77

8+
/* SysTick timing */
9+
volatile uint32_t g_tick = 0;
10+
volatile uint8_t g_waiting = 0;
11+
volatile uint8_t g_tickOverflow = 0;
12+
13+
void SysTick_Handler()
14+
{
15+
uint32_t tickBefore = g_tick++;
16+
if (g_waiting) {
17+
if (tickBefore > g_tick)
18+
g_tickOverflow = 1;
19+
}
20+
}
21+
22+
uint32_t Board_GetTick(void)
23+
{
24+
return g_tick;
25+
}
26+
27+
whal_Timeout g_whalTimeout = {
28+
.timeoutTicks = 5, /* 5ms timeout */
29+
.GetTick = Board_GetTick,
30+
};
31+
832
/* Clock */
933
whal_Clock g_whalClock = {
1034
WHAL_STM32WB55_RCC_PLL_DEVICE,
@@ -95,6 +119,7 @@ whal_Uart g_whalUart = {
95119
.cfg = &(whal_Stm32wbUart_Cfg) {
96120
.clkCtrl = &g_whalClock,
97121
.clk = &(whal_Stm32wbRcc_Clk) {WHAL_STM32WB55_UART1_CLOCK},
122+
.timeout = &g_whalTimeout,
98123

99124
.baud = 115200,
100125
},
@@ -107,6 +132,7 @@ whal_Flash g_whalFlash = {
107132
.cfg = &(whal_Stm32wbFlash_Cfg) {
108133
.clkCtrl = &g_whalClock,
109134
.clk = &(whal_Stm32wbRcc_Clk) {WHAL_STM32WB55_FLASH_CLOCK},
135+
.timeout = &g_whalTimeout,
110136

111137
.startAddr = 0x08000000,
112138
.size = 0x100000,
@@ -120,6 +146,7 @@ whal_Rng g_whalRng = {
120146
.cfg = &(whal_Stm32wbRng_Cfg) {
121147
.clkCtrl = &g_whalClock,
122148
.clk = &(whal_Stm32wbRcc_Clk) {WHAL_STM32WB55_RNG_CLOCK},
149+
.timeout = &g_whalTimeout,
123150
},
124151
};
125152

@@ -142,31 +169,18 @@ whal_Crypto g_whalCrypto = {
142169
.cfg = &(whal_Stm32wbAes_Cfg) {
143170
.clkCtrl = &g_whalClock,
144171
.clk = &(whal_Stm32wbRcc_Clk) {WHAL_STM32WB55_AES1_CLOCK},
172+
.timeout = &g_whalTimeout,
145173
},
146174
};
147175

148-
/* SysTick timing */
149-
volatile size_t g_tick = 0;
150-
volatile uint8_t g_waiting = 0;
151-
volatile uint8_t g_tickOverflow = 0;
152-
153-
void SysTick_Handler()
154-
{
155-
size_t tickBefore = g_tick++;
156-
if (g_waiting) {
157-
if (tickBefore > g_tick)
158-
g_tickOverflow = 1;
159-
}
160-
}
161-
162176
void Board_WaitMs(size_t ms)
163177
{
164-
size_t startCount = g_tick;
178+
uint32_t startCount = g_tick;
165179
g_waiting = 1;
166180
while (1) {
167-
size_t currentCount = g_tick;
181+
uint32_t currentCount = g_tick;
168182
if (g_tickOverflow) {
169-
if ((SIZE_MAX - startCount) + currentCount > ms) {
183+
if ((UINT32_MAX - startCount) + currentCount > ms) {
170184
break;
171185
}
172186
} else if (currentCount - startCount > ms) {

boards/stm32wb55xx_nucleo/board.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ extern whal_Flash g_whalFlash;
1313
extern whal_Rng g_whalRng;
1414
extern whal_Crypto g_whalCrypto;
1515

16-
extern volatile size_t g_tick;
16+
extern volatile uint32_t g_tick;
1717

1818
#define BOARD_LED_PIN 0
1919
#define BOARD_FLASH_TEST_ADDR 0x08080000

docs/adding_a_board.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ Exports global peripheral instances and board-specific constants:
1919

2020
#include <wolfHAL/wolfHAL.h>
2121

22-
extern whal_Clock g_whalClock;
23-
extern whal_Gpio g_whalGpio;
24-
extern whal_Uart g_whalUart;
25-
extern whal_Timer g_whalTimer;
26-
extern whal_Flash g_whalFlash;
22+
extern whal_Clock g_whalClock;
23+
extern whal_Gpio g_whalGpio;
24+
extern whal_Uart g_whalUart;
25+
extern whal_Timer g_whalTimer;
26+
extern whal_Flash g_whalFlash;
27+
extern whal_Timeout g_whalTimeout;
2728

2829
#define BOARD_LED_PIN 0
2930

docs/getting_started.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ whal_Uart g_whalUart = {
126126
.cfg = &(whal_Stm32wbUart_Cfg) {
127127
.clkCtrl = &g_whalClock,
128128
.clk = &(whal_Stm32wbRcc_Clk){WHAL_STM32WB55_UART1_CLOCK},
129+
.timeout = &g_whalTimeout,
129130
.baud = 115200,
130131
},
131132
};
@@ -202,6 +203,7 @@ operation completed. The error codes are:
202203
| `WHAL_EINVAL` | Invalid argument or unsupported operation |
203204
| `WHAL_ENOTREADY` | Resource is busy or not yet available |
204205
| `WHAL_EHARDWARE` | Hardware error (e.g., RNG entropy failure) |
206+
| `WHAL_ETIMEOUT` | Operation timed out waiting for hardware |
205207

206208
## Optimizing for Size
207209

docs/writing_a_driver.md

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,124 @@ Use `Write` and `Read` for whole-register access. Use `Update` and `Get` when
6262
you need to modify or read individual fields within a register without
6363
disturbing other bits.
6464
65+
### Timeouts
66+
67+
Most drivers poll hardware status registers in busy-wait loops (e.g., waiting
68+
for a DMA transfer to complete, a flash erase to finish, or an AES computation
69+
to produce a result). wolfHAL provides an optional timeout mechanism to bound
70+
these waits and prevent infinite hangs if hardware misbehaves.
71+
72+
#### Timeout Struct
73+
74+
The board creates a `whal_Timeout` instance with a tick source and a timeout
75+
duration. Drivers receive a pointer to it through their configuration struct:
76+
77+
```c
78+
typedef struct {
79+
uint32_t timeoutTicks; /* max ticks before timeout */
80+
uint32_t startTick; /* snapshot, set by START */
81+
uint32_t (*GetTick)(void); /* board-provided tick source */
82+
} whal_Timeout;
83+
```
84+
85+
The tick units are determined by the board's `GetTick` implementation. A 1 kHz
86+
SysTick gives millisecond ticks; a 1 MHz timer gives microsecond ticks. Drivers
87+
do not need to know the tick rate.
88+
89+
#### whal_Reg_ReadPoll
90+
91+
For the common case of polling a register bit, use `whal_Reg_ReadPoll` from
92+
`wolfHAL/regmap.h`:
93+
94+
```c
95+
whal_Error whal_Reg_ReadPoll(size_t base, size_t offset,
96+
size_t mask, size_t value,
97+
whal_Timeout *timeout);
98+
```
99+
100+
This polls until `(reg & mask) == value` or the timeout expires. Pass the mask
101+
as the value to wait for bits to be set, or `0` to wait for bits to be clear:
102+
103+
```c
104+
/* Wait for TXE flag to be set */
105+
err = whal_Reg_ReadPoll(base, SPI_SR_REG, SPI_SR_TXE_Msk,
106+
SPI_SR_TXE_Msk, cfg->timeout);
107+
108+
/* Wait for BSY flag to be clear */
109+
err = whal_Reg_ReadPoll(base, SPI_SR_REG, SPI_SR_BSY_Msk,
110+
0, cfg->timeout);
111+
```
112+
113+
A NULL timeout pointer means unbounded wait (poll forever).
114+
115+
#### Driver-Specific Helpers
116+
117+
When a driver has many polling sites that also need post-poll cleanup (e.g.,
118+
clearing a flag), wrap the pattern in a local helper to avoid code duplication:
119+
120+
```c
121+
static whal_Error WaitForCCF(size_t base, whal_Timeout *timeout)
122+
{
123+
whal_Error err;
124+
err = whal_Reg_ReadPoll(base, AES_SR_REG, AES_SR_CCF_Msk,
125+
AES_SR_CCF_Msk, timeout);
126+
if (err)
127+
return err;
128+
whal_Reg_Update(base, AES_CR_REG, AES_CR_CCFC_Msk, AES_CR_CCFC_Msk);
129+
return WHAL_SUCCESS;
130+
}
131+
```
132+
133+
This keeps code size small — one function body shared across all call sites
134+
instead of inlined polling loops at each location.
135+
136+
#### Cleanup on Timeout
137+
138+
When a timeout occurs during an operation that has enabled a hardware mode
139+
(e.g., flash programming mode, AES enable), the driver must still clean up
140+
before returning. Use a `goto cleanup` pattern:
141+
142+
```c
143+
whal_Error err = WHAL_SUCCESS;
144+
145+
whal_Reg_Update(base, CR_REG, PG_Msk, 1); /* enable programming mode */
146+
147+
for (...) {
148+
err = whal_Reg_ReadPoll(base, SR_REG, BSY_Msk, 0, cfg->timeout);
149+
if (err)
150+
goto cleanup;
151+
}
152+
153+
cleanup:
154+
whal_Reg_Update(base, CR_REG, PG_Msk, 0); /* always disable */
155+
return err;
156+
```
157+
158+
Never return directly from inside a polling loop if the peripheral is in a
159+
mode that requires cleanup.
160+
161+
#### Compile-Time Disable
162+
163+
Define `WHAL_CFG_NO_TIMEOUT` to remove all timeout logic from the binary.
164+
When defined, `WHAL_TIMEOUT_START` becomes a no-op and `WHAL_TIMEOUT_EXPIRED`
165+
always evaluates to `0`, so polling loops run until the hardware condition is
166+
met with no overhead.
167+
168+
#### Adding Timeout to a Config Struct
169+
170+
Driver config structs that use polling should include an optional timeout
171+
pointer:
172+
173+
```c
174+
typedef struct {
175+
/* ... other config fields ... */
176+
whal_Timeout *timeout;
177+
} whal_MyplatformFoo_Cfg;
178+
```
179+
180+
The timeout is optional — if the board does not set it (NULL), all waits are
181+
unbounded.
182+
65183
### Avoiding Bloat
66184

67185
When a peripheral has multiple distinct operating modes or configurations,

0 commit comments

Comments
 (0)