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
2 changes: 1 addition & 1 deletion .github/workflows/core-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
extra_cflags: ["", "-DWHAL_CFG_NO_TIMEOUT"]
extra_cflags: ["", "-DWHAL_CFG_NO_TIMEOUT", "-DWHAL_CFG_64BIT_TICK"]
Comment thread
aidangarske marked this conversation as resolved.
steps:
- uses: actions/checkout@v4

Expand Down
24 changes: 24 additions & 0 deletions docs/writing_a_driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ 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.

The tick fields are 32-bit by default; define `WHAL_CFG_64BIT_TICK` to widen
them to 64-bit (see "64-bit Ticks" below).

#### whal_Reg_ReadPoll

For the common case of polling a register bit, use `whal_Reg_ReadPoll` from
Expand Down Expand Up @@ -325,6 +328,27 @@ 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.

#### 64-bit Ticks

By default the tick fields (`timeoutTicks`, `startTick`, and the `GetTick`
return value) are 32-bit, which wraps in about 49 days at millisecond
resolution. Define `WHAL_CFG_64BIT_TICK` to widen them to `uint64_t` for
systems that need a longer monotonic range. The elapsed-time comparison in
`WHAL_TIMEOUT_EXPIRED` uses unsigned wraparound arithmetic that adjusts to the
configured width, so both settings handle a tick-counter wrap correctly.

When the macro is defined, the board's tick source must match the width: its
`g_tick` counter and `GetTick` callback (typically `Board_GetTick`) must be
`uint64_t`, since `whal_Timeout.GetTick` becomes `uint64_t (*)(void)`. The
`WHAL_TICK_MAX` macro in `wolfHAL/timeout.h` evaluates to the maximum tick
value at the configured width (`UINT32_MAX` or `UINT64_MAX`) for wrap-handling
logic.

A 64-bit `GetTick` must return a coherent snapshot. On a 32-bit MCU a 64-bit
read is two loads, so a tick interrupt landing between them returns a torn value
and `WHAL_TIMEOUT_EXPIRED` reports a spurious timeout. Read with interrupts
masked, or retry until the halves agree.

#### Adding Timeout to a Config Struct

Driver config structs that use polling should include an optional timeout
Expand Down
15 changes: 12 additions & 3 deletions tests/core/test_timeout.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,21 @@

#ifndef WHAL_CFG_NO_TIMEOUT

#ifdef WHAL_CFG_64BIT_TICK
Comment thread
AlexLanzano marked this conversation as resolved.
static uint64_t g_fakeTick;

static uint64_t FakeTick(void)
{
return g_fakeTick;
}
#else
static uint32_t g_fakeTick;

static uint32_t FakeTick(void)
{
return g_fakeTick;
}
#endif

static whal_Timeout g_timeout = {
Comment thread
aidangarske marked this conversation as resolved.
.timeoutTicks = 10,
Expand Down Expand Up @@ -134,12 +143,12 @@ static void Test_Timeout_TickWrap(void)
{
int expired;

g_fakeTick = UINT32_MAX - 3;
g_fakeTick = WHAL_TICK_MAX - 3;
WHAL_TIMEOUT_START(timeout());
g_fakeTick = UINT32_MAX;
g_fakeTick = WHAL_TICK_MAX;
expired = WHAL_TIMEOUT_EXPIRED(timeout());
WHAL_ASSERT_EQ(expired, 0);
g_fakeTick = UINT32_MAX - 3 + 10;
g_fakeTick = WHAL_TICK_MAX - 3 + 10;
expired = WHAL_TIMEOUT_EXPIRED(timeout());
WHAL_ASSERT_NEQ(expired, 0);
}
Expand Down
19 changes: 19 additions & 0 deletions wolfHAL/timeout.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,24 @@
* defined, all timeout operations compile away completely.
*/

/* Tick width. Define WHAL_CFG_64BIT_TICK to widen ticks to 64-bit; the default
* is 32-bit. A board's g_tick / GetTick source must match this width. */
#ifdef WHAL_CFG_64BIT_TICK
Comment thread
aidangarske marked this conversation as resolved.
#define WHAL_TICK_MAX UINT64_MAX
#else
#define WHAL_TICK_MAX UINT32_MAX
#endif
Comment thread
AlexLanzano marked this conversation as resolved.

typedef struct {
Comment thread
aidangarske marked this conversation as resolved.
#ifdef WHAL_CFG_64BIT_TICK
uint64_t timeoutTicks;
uint64_t startTick;
uint64_t (*GetTick)(void);
#else
uint32_t timeoutTicks;
uint32_t startTick;
uint32_t (*GetTick)(void);
#endif
} whal_Timeout;

#ifdef WHAL_CFG_NO_TIMEOUT
Expand All @@ -66,8 +80,13 @@ typedef struct {
*
* Safe to call with a NULL pointer — returns 0 (not expired).
*/
#ifdef WHAL_CFG_64BIT_TICK
#define WHAL_TIMEOUT_EXPIRED(t) \
((t) && ((uint64_t)((t)->GetTick() - (t)->startTick) >= (t)->timeoutTicks))
Comment thread
aidangarske marked this conversation as resolved.
#else
#define WHAL_TIMEOUT_EXPIRED(t) \
((t) && ((uint32_t)((t)->GetTick() - (t)->startTick) >= (t)->timeoutTicks))
#endif

#endif /* WHAL_CFG_NO_TIMEOUT */

Expand Down
Loading