From 5993dd6ef5765a49929257d1cc94b7f37e215f3f Mon Sep 17 00:00:00 2001 From: Martin Roth Date: Sat, 5 Apr 2025 14:58:22 -0600 Subject: [PATCH] Documentation: Add Timers, Stopwatch, and Delays Change-Id: I3b58817c1ed06e6d7d5d5668b0e38ec8cfedf122 Signed-off-by: Martin Roth Reviewed-on: https://review.coreboot.org/c/coreboot/+/87187 Reviewed-by: Matt DeVillier Tested-by: build bot (Jenkins) --- Documentation/lib/index.md | 1 + Documentation/lib/stopwatch.md | 910 +++++++++++++++++++++++++++++++++ 2 files changed, 911 insertions(+) create mode 100644 Documentation/lib/stopwatch.md diff --git a/Documentation/lib/index.md b/Documentation/lib/index.md index f75408dd5f..b0eaf437b1 100644 --- a/Documentation/lib/index.md +++ b/Documentation/lib/index.md @@ -11,4 +11,5 @@ ABI data consumption Timestamps Firmware Configuration Interface Relocatable Modules +Timers, Stopwatch, and Delays ``` diff --git a/Documentation/lib/stopwatch.md b/Documentation/lib/stopwatch.md new file mode 100644 index 0000000000..94817e8f07 --- /dev/null +++ b/Documentation/lib/stopwatch.md @@ -0,0 +1,910 @@ +# coreboot Timers, Stopwatch, Delays, and Associated Callbacks + +## Introduction + +coreboot provides several mechanisms for handling time, including +high-precision stopwatches for profiling, simple delay functions for +hardware timing, and a monotonic timer system that forms the foundation +for these features. It also supports scheduling timer callbacks for +deferred execution. These tools are crucial for performance +optimization, debugging, ensuring proper hardware timing, and managing +asynchronous events during the boot process. + +This document describes the core monotonic timer, the delay functions +(`udelay`, `mdelay`, `delay`), the stopwatch API (`struct stopwatch`), +and the timer callback mechanism. + + +## Architecture Overview + +The timing facilities are layered: + +- **Platform Timer:** Hardware-specific implementations provide a raw + time source (e.g., LAPIC timer, Time Base register). These are often + abstracted by `timer_monotonic_get()`. +- **Monotonic Timer:** (`src/include/timer.h`, `src/lib/timer.c`) + Provides a consistent time source (`struct mono_time`) counting + microseconds since timer initialization. Requires + `CONFIG(HAVE_MONOTONIC_TIMER)`. +- **Delay Functions:** (`src/include/delay.h`, `src/lib/delay.c`, + `src/lib/timer.c`) Simple blocking delays (`udelay`, `mdelay`, + `delay`). The generic `udelay` uses the stopwatch API if yielding via + `thread_yield_microseconds()` is not performed. +- **Stopwatch API:** (`src/include/timer.h`) A lightweight wrapper + around the monotonic timer for measuring durations and handling + timeouts (`struct stopwatch`,`wait_us`,`wait_ms`). +- **Timer Callbacks:** (`src/include/timer.h`) Allows scheduling + functions to be called after a specified delay + (`struct timeout_callback`, `timer_sched_callback()`, `timers_run()`). + +These APIs are generally designed to be thread-safe and usable across +different boot stages. + + +## Core Monotonic Timer + +The foundation is the monotonic timer, enabled by +`CONFIG(HAVE_MONOTONIC_TIMER)`. It provides a time source that +continuously increases from an arbitrary starting point (usually near +timer initialization). + +### Data Structures + +#### struct mono_time + +```c +struct mono_time { + uint64_t microseconds; // Time in microseconds since timer init +}; +``` +Represents a point in monotonic time. Direct field access outside core +timer code is discouraged; use helper functions. + +### API Functions + +#### timer_monotonic_get + +```c +void timer_monotonic_get(struct mono_time *mt); +``` +Retrieves the current monotonic time. + +**Parameters:** +- `mt`: Pointer to a `struct mono_time` to store the current time. + +**Preconditions:** +- `CONFIG(HAVE_MONOTONIC_TIMER)` must be enabled. +- The underlying platform timer must be functional. Platform + implementations may require an `init_timer()` call. + +**Postconditions:** +- `mt` contains the current monotonic time in microseconds. + +**Note:** If `CONFIG(HAVE_MONOTONIC_TIMER)` is *not* enabled, functions +relying on it (like stopwatch functions) will generally fall back to +basic behavior (e.g., returning 0 durations, immediate expiration +checks). + +#### init_timer + +```c +void init_timer(void); +``` + +Platform-specific timer initialization. The generic version in +`src/lib/timer.c` is a weak empty function +`__weak void init_timer(void) { /* do nothing */ }`. Platforms needing +explicit timer setup (e.g., configuring frequency, enabling the +counter) must provide their own strong implementation. Check platform +code (`src/cpu/`, `src/soc/`) for details. + +#### `mono_time` Helper Functions + +`src/include/timer.h` also provides several inline helper functions for +manipulating `struct mono_time` values: +- `mono_time_set_usecs()`, `mono_time_set_msecs()`: Set time. +- `mono_time_add_usecs()`, `mono_time_add_msecs()`: Add duration. +- `mono_time_cmp()`: Compare two times. +- `mono_time_after()`, `mono_time_before()`: Check time ordering. +- `mono_time_diff_microseconds()`: Calculate difference between two + times. + +Refer to `src/include/timer.h` for their exact definitions. + + +## Delay Functions + +coreboot provides simple functions for introducing delays. + +### udelay + +```c +void udelay(unsigned int usecs); +``` +Delays execution for *at least* the specified number of microseconds. + +**Parameters:** +- `usecs`: Number of microseconds to delay. + +**Implementation Notes:** +- The generic implementation in `src/lib/timer.c` first attempts to + yield using `thread_yield_microseconds()` if `CONFIG(HAS_THREADS)` is + enabled. If yielding handles the required delay (function returns 0), + `udelay` returns immediately, allowing other threads to run. +- If `thread_yield_microseconds()` does not handle the delay (returns + non-zero), or if threading is disabled, the generic `udelay` falls + back to a busy-wait using the stopwatch API + (`stopwatch_init_usecs_expire()` and + `stopwatch_wait_until_expired()`). +- Architecture-specific implementations (e.g., in `src/arch/`) may + override the generic one for better precision or efficiency. +- Adds 1 microsecond internally to ensure the delay is *at least* the + requested duration. + +**Preconditions:** +- If relying on the stopwatch fallback, + `CONFIG(HAVE_MONOTONIC_TIMER)` is needed. +- Underlying timer (if used) must be initialized (potentially via a + platform `init_timer()`). +- The delay value should be reasonable (overly large values might cause + issues or unexpected behavior depending on the implementation). + +**Example:** +```c +#include + +// Wait for 100 microseconds +udelay(100); +``` + +### mdelay + +```c +void mdelay(unsigned int msecs); +``` +Delays execution for *at least* the specified number of milliseconds. + +**Parameters:** +- `msecs`: Number of milliseconds to delay. + +**Implementation Notes:** +- The generic implementation in `src/lib/delay.c` simply calls + `udelay(1000)` in a loop `msecs` times. +- Therefore, it inherits the behavior of `udelay`, including the attempt + to yield first if `udelay` supports it. + +**Preconditions:** +- Same as `udelay`. + +**Example:** +```c +#include + +// Wait for 50 milliseconds +mdelay(50); +``` + +### delay + +```c +void delay(unsigned int secs); +``` +Delays execution for *at least* the specified number of seconds. + +**Parameters:** +- `secs`: Number of seconds to delay. + +**Implementation Notes:** +- The generic implementation in `src/lib/delay.c` simply calls + `mdelay(1000)` in a loop `secs` times. +- Inherits the behavior of `mdelay` and `udelay`. + +**Preconditions:** +- Same as `udelay`. + +**Example:** +```c +#include + +// Wait for 2 seconds +delay(2); +``` + + +## Stopwatch API + +The stopwatch API provides a convenient way to measure time durations +and implement timeouts based on the monotonic timer. + +### Data Structures + +#### struct stopwatch + +```c +#include // For struct stopwatch and struct mono_time + +struct stopwatch { + struct mono_time start; // Time when stopwatch was started or initialized + struct mono_time current; // Time when stopwatch was last ticked + struct mono_time expires; // Expiration time for timeout operations +}; +``` +Holds the state for a stopwatch instance. + +### API Functions + +#### Initialization Functions + +##### stopwatch_init + +```c +#include + +static inline void stopwatch_init(struct stopwatch *sw); +``` +Initializes a stopwatch structure. `start`, `current`, and `expires` are +all set to the current monotonic time. Use this when you only need to +measure elapsed duration from initialization. + +**Parameters:** +- `sw`: Pointer to the stopwatch structure to initialize. + +**Preconditions:** +- `sw` must point to valid memory. +- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled for meaningful + timing. + +**Postconditions:** +- The stopwatch is initialized. + + +##### stopwatch_init_usecs_expire + +```c +#include + +static inline void stopwatch_init_usecs_expire(struct stopwatch *sw, uint64_t us); +``` +Initializes a stopwatch and sets an expiration time `us` microseconds +from now. + +**Parameters:** +- `sw`: Pointer to the stopwatch structure. +- `us`: Timeout duration in microseconds. + +**Preconditions:** +- `sw` must point to valid memory. +- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled. + +**Postconditions:** +- The stopwatch is initialized, and `expires` is set `us` microseconds + after `start`. + + +##### stopwatch_init_msecs_expire + +```c +#include + +static inline void stopwatch_init_msecs_expire(struct stopwatch *sw, uint64_t ms); +``` +Initializes a stopwatch and sets an expiration time `ms` milliseconds +from now. + +**Parameters:** +- `sw`: Pointer to the stopwatch structure. +- `ms`: Timeout duration in milliseconds. + +**Preconditions:** +- `sw` must point to valid memory. +- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled. + +**Postconditions:** +- The stopwatch is initialized, and `expires` is set `ms` milliseconds + after `start`. + + +#### Time Measurement Functions + +##### stopwatch_tick + +```c +#include + +static inline void stopwatch_tick(struct stopwatch *sw); +``` +Updates the `current` time field in the stopwatch structure to the +current monotonic time. This is often called implicitly by other +stopwatch functions like `stopwatch_expired()` and +`stopwatch_duration_usecs()`. + +**Parameters:** +- `sw`: Pointer to the stopwatch structure. + +**Preconditions:** +- The stopwatch must be initialized. +- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled. + + +##### stopwatch_expired + +```c +#include + +static inline int stopwatch_expired(struct stopwatch *sw); +``` +Checks if the stopwatch's expiration time (`expires`) has passed. It +implicitly calls `stopwatch_tick()` to get the current time for +comparison. + +**Parameters:** +- `sw`: Pointer to the stopwatch structure. + +**Returns:** +- Non-zero (true) if `current` time >= `expires` time. +- Zero (false) otherwise. + +**Preconditions:** +- The stopwatch must be initialized (preferably with an expiration + time). +- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled. + + +##### stopwatch_wait_until_expired + +```c +#include + +static inline void stopwatch_wait_until_expired(struct stopwatch *sw); +``` +Blocks (busy-waits) until the stopwatch expires by repeatedly calling +`stopwatch_expired()`. + +**Parameters:** +- `sw`: Pointer to the stopwatch structure. + +**Preconditions:** +- The stopwatch must be initialized with an expiration time. +- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled. + +**Postconditions:** +- The function returns only after the stopwatch has expired. + + +#### Duration Measurement Functions + +##### stopwatch_duration_usecs + +```c +#include + +static inline int64_t stopwatch_duration_usecs(struct stopwatch *sw); +``` +Returns the elapsed time in microseconds between `start` and `current`. +If `current` hasn't been updated since `stopwatch_init`, it implicitly +calls `stopwatch_tick()` first. + +**Parameters:** +- `sw`: Pointer to the stopwatch structure. + +**Returns:** +- Elapsed time in microseconds (`current` - `start`). + +**Preconditions:** +- The stopwatch must be initialized. +- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled for a meaningful + duration. + + +##### stopwatch_duration_msecs + +```c +#include + +static inline int64_t stopwatch_duration_msecs(struct stopwatch *sw); +``` +Returns the elapsed time in milliseconds since the stopwatch was +started. It calls `stopwatch_duration_usecs()` and divides by 1000. + +**Parameters:** +- `sw`: Pointer to the stopwatch structure. + +**Returns:** +- Elapsed time in milliseconds. + +**Preconditions:** +- The stopwatch must be initialized. +- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled. + + +### Utility Macros + +These macros combine stopwatch initialization and expiration checking with +a user-provided condition. + +#### wait_us + +```c +#include + +#define wait_us(timeout_us, condition) ... +``` +Waits until a condition becomes true or a timeout elapses, whichever +comes first. Internally uses a `struct stopwatch`. + +**Parameters:** +- `timeout_us`: Timeout duration in microseconds. +- `condition`: A C expression that evaluates to true or false. The loop + continues as long as the condition is false and the timeout has not + expired. + +**Returns:** +- 0: If the condition was still false when the timeout expired. +- >0: If the condition became true before the timeout. The return value + is the approximate number of microseconds waited (at least 1). + +**Preconditions:** +- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled. + + +#### wait_ms + +```c +#include + +#define wait_ms(timeout_ms, condition) ... +``` +Similar to `wait_us`, but the timeout is specified in milliseconds. + +**Parameters:** +- `timeout_ms`: Timeout duration in milliseconds. +- `condition`: C expression to wait for. + +**Returns:** +- 0: If the condition was still false after the timeout. +- >0: If the condition became true before the timeout. The return value + is the approximate number of milliseconds waited (rounded up, at + least 1). + +**Preconditions:** +- `CONFIG(HAVE_MONOTONIC_TIMER)` should be enabled. + + +## Key differences between `mdelay` and `wait_ms` + +While both can be used to wait, they serve different purposes: + +### Purpose and Use Case + +- `mdelay`: Provides an *unconditional* delay for a fixed duration. The + generic implementation attempts to yield to other threads first (if + `CONFIG(HAS_THREADS)` is enabled via `udelay`), but it always waits + for (at least) the specified time, potentially using a busy-wait if + yielding doesn't occur. Primarily used for hardware timing + requirements or pacing operations. + +- `wait_ms`: Provides a *conditional* wait. It waits for a specific C + expression (`condition`) to become true, but *only up to* a maximum + timeout duration. Used when polling for a status change or event, with + a safeguard against waiting indefinitely. + +### When to Use Which + +#### Use `mdelay` when: + +- You need a guaranteed minimum delay (e.g., waiting for hardware to + settle after a register write). +- You are implementing a hardware initialization sequence with specific + timing requirements between steps. +- You want a simple pause, potentially allowing other threads to run if + the underlying `udelay` yields. + +**Example:** +```c +#include +#include // For write_reg hypothetical function + +// Initialize hardware with specific timing requirements +write_reg(REG_A, value); +mdelay(10); // Must wait (at least) 10ms after writing REG_A +write_reg(REG_B, another_value); +``` + +#### Use `wait_ms` when: + +- You are waiting for a hardware status bit to change or a condition to + become true. +- You need to implement a timeout for an operation that might fail or + take too long. +- You want to know approximately how long you waited for the condition + to become true (via the return value). + +**Example:** +```c +#include +#include +#include // For read_reg hypothetical function + +// Wait for hardware to become ready, but not forever +#define STATUS_REG 0x100 +#define READY_BIT (1 << 0) +int64_t waited_ms = wait_ms(100, (read_reg(STATUS_REG) & READY_BIT)); + +if (waited_ms > 0) { + printk(BIOS_INFO, "Hardware ready after ~%lld ms\n", waited_ms); +} else { + printk(BIOS_ERR, "Timeout: Hardware failed to become ready within 100 ms\n"); +} +``` + +### Performance Considerations + +- `mdelay`: Generally simpler if the underlying `udelay` performs a + busy-wait. If it yields (`thread_yield_microseconds`), overhead depends + on the scheduler. Always waits for the full duration (approximately). +- `wait_ms`: Involves repeated condition checking and stopwatch + management (`stopwatch_expired`, which calls `timer_monotonic_get`), + adding overhead within the loop. However, it can return early if the + condition becomes true, potentially saving time compared to a fixed + `mdelay`. + +### Error Handling / Feedback + +- `mdelay`: Provides no feedback. It simply waits. +- `wait_ms`: Returns whether the condition was met within the timeout + and provides the approximate wait time if successful. This allows for + explicit timeout handling. + +### Summary + +Use `mdelay` for fixed, unconditional delays, potentially allowing +thread yielding. Use `wait_ms` for conditional waits with a timeout and +feedback on success or failure. Choose based on whether you need to poll +for a condition or simply need to pause execution. + + +## Timer Callbacks + +coreboot provides a mechanism to schedule functions (callbacks) to be +executed after a certain time has elapsed. This requires +`CONFIG(TIMER_QUEUE)`. + +### Data Structures + +#### struct timeout_callback + +```c +#include + +struct timeout_callback { + void *priv; // Private data for the callback + void (*callback)(struct timeout_callback *tocb); // Function to call + /* Internal use by timer library: */ + struct mono_time expiration; // Calculated expiration time +}; +``` + +Represents a scheduled callback. The user initializes `priv` and +`callback`. + +### API Functions + +#### timer_sched_callback + +```c +#include + +int timer_sched_callback(struct timeout_callback *tocb, uint64_t us); +``` +Schedules a callback function to run after a specified delay. + +**Parameters:** +- `tocb`: Pointer to a `struct timeout_callback`. The `priv` and + `callback` fields must be set by the caller. The structure must + persist until the callback runs or is canceled (cancellation not + directly supported by API). +- `us`: Delay in microseconds from the time of this call until the + callback should run. + +**Returns:** +- 0: Success. +- <0: Error (e.g., timer queue full, invalid arguments). + +**Preconditions:** +- `CONFIG(TIMER_QUEUE)` and `CONFIG(HAVE_MONOTONIC_TIMER)` must be + enabled. +- `tocb` must point to a valid structure with `callback` assigned. + +**Postconditions:** +- The callback is added to a queue, to be executed when `timers_run()` + is called after the expiration time. + + +#### timers_run + +```c +#include + +int timers_run(void); +``` + +Checks the timer callback queue and executes any callbacks whose +expiration time has passed. This function needs to be called +periodically from the main execution flow (e.g., in a boot state loop +or idle task) for callbacks to be processed. + +**Returns:** +- 1: If callbacks were run or are still pending in the queue. +- 0: If the queue is empty and no callbacks were run. + +**Preconditions:** +- `CONFIG(TIMER_QUEUE)` and `CONFIG(HAVE_MONOTONIC_TIMER)` must be + enabled. + +**Usage:** +Typically called in loops where deferred work might need processing: +```c +#include + +// Example main loop structure +while (some_condition) { + // ... do other work ... + + // Process any expired timer callbacks + timers_run(); + + // ... potentially yield or sleep ... +} +``` + + +## Usage Examples + +### Basic Timing Measurement Example + +```c +#include +#include + +struct stopwatch sw; +stopwatch_init(&sw); + +// ---> Code section to measure start +// ... perform some operations ... +// ---> Code section to measure end + +// stopwatch_duration_usecs implicitly ticks the stopwatch if needed +int64_t duration_us = stopwatch_duration_usecs(&sw); +printk(BIOS_INFO, "Operation took %lld microseconds\n", duration_us); + +// Or in milliseconds +int64_t duration_ms = stopwatch_duration_msecs(&sw); +printk(BIOS_INFO, "Operation took %lld milliseconds\n", duration_ms); +``` + + +### Timeout Operation (Polling) Example + +```c +#include +#include +#include // For uint8_t example + +// Hypothetical hardware status check +extern uint8_t check_hardware_status(void); +#define HW_READY (1 << 0) + +struct stopwatch sw; +// Initialize stopwatch with a 100 millisecond timeout +stopwatch_init_msecs_expire(&sw, 100); + +uint8_t status = 0; +while (!(status & HW_READY)) { + status = check_hardware_status(); + + if (stopwatch_expired(&sw)) { + printk(BIOS_ERR, "Operation timed out waiting for HW_READY\n"); + // Handle timeout error... + status = 0; // Indicate failure perhaps + break; + } + // Optional: Add a small delay here to avoid busy-spinning the CPU excessively + // udelay(10); // e.g., wait 10us between checks +} + +if (status & HW_READY) { + printk(BIOS_DEBUG, "Hardware became ready.\n"); + // Continue with operation... +} +``` + + +### Waiting for a Condition (Using `wait_ms`) Example + +```c +#include +#include +#include // For uint8_t example + +// Hypothetical hardware status check +extern uint8_t check_hardware_status(void); +#define HW_READY (1 << 0) + +// Wait up to 100ms for the HW_READY bit +int64_t waited_ms = wait_ms(100, (check_hardware_status() & HW_READY)); + +if (waited_ms > 0) { + printk(BIOS_INFO, "Condition met: HW_READY asserted after ~%lld ms\n", waited_ms); + // Continue... +} else { + printk(BIOS_ERR, "Timeout: HW_READY not asserted within 100 ms\n"); + // Handle timeout error... +} +``` + +### Scheduling a Timer Callback Example + +```c +#include +#include + +// Data structure for our callback context +struct my_callback_data { + int counter; + const char *message; +}; + +// The callback function +static void my_callback_handler(struct timeout_callback *tocb) +{ + struct my_callback_data *data = tocb->priv; + printk(BIOS_INFO, "Callback executed! Message: %s, Counter: %d\n", + data->message, data->counter); + // Note: 'tocb' can be reused here to reschedule if needed, + // or the memory it points to can be freed if dynamically allocated. +} + +// Somewhere in initialization code: +static struct timeout_callback my_timer; +static struct my_callback_data my_data; + +void schedule_my_task(void) +{ + my_data.counter = 42; + my_data.message = "Hello from timer"; + + my_timer.priv = &my_data; + my_timer.callback = my_callback_handler; + + // Schedule the callback to run after 500 milliseconds (500,000 us) + int rc = timer_sched_callback(&my_timer, 500 * 1000); + if (rc == 0) { + printk(BIOS_DEBUG, "Scheduled my_callback_handler successfully.\n"); + } else { + printk(BIOS_ERR, "Failed to schedule callback!\n"); + } +} + +// Remember that timers_run() must be called periodically elsewhere +// for the callback to actually execute after the delay. +``` + +## Best Practices + +1. **Enable Monotonic Timer:** For accurate timing and stopwatch + functionality, ensure `CONFIG(HAVE_MONOTONIC_TIMER)` is enabled and a + suitable platform timer is configured. +2. **Minimize Stopwatch Overhead:** While lightweight, frequent calls, + especially `timer_monotonic_get()` implicitly called by stopwatch + functions, can add up. Measure larger, significant code blocks rather + than tiny ones in tight loops unless absolutely necessary. +3. **Use Appropriate Time Units:** Choose `usecs` or `msecs` variants + based on the scale of time you are dealing with. +4. **Handle Timeouts Gracefully:** When using expiration or + `wait_ms`/`wait_us`, always check the result and handle the timeout + case appropriately. Don't assume success. +5. **Process Timer Callbacks:** If using `timer_sched_callback`, ensure + `timers_run()` is called regularly in your main processing loops or + idle states. +6. **Avoid `mdelay` for Polling:** Prefer `wait_ms` or manual polling + with `stopwatch_expired` when waiting for conditions, as `mdelay` + offers no timeout handling or feedback. +7. **Consider Platform Accuracy:** Timer resolution and accuracy vary + between platforms. Don't assume microsecond precision unless verified + for the target hardware. + + +## Limitations + +1. **Monotonic Timer Dependence:** Stopwatch and timer callbacks rely + heavily on `CONFIG(HAVE_MONOTONIC_TIMER)`. Without it, their + behavior is limited. +2. **Timer Resolution:** Accuracy is limited by the underlying platform + timer's resolution and the frequency of `timer_monotonic_get()` + updates internally. +3. **64-bit Microsecond Counter:** While large, the + `uint64_t microseconds` counter will eventually wrap around + (after ~584,000 years), though this is not a practical concern for + boot times. More relevant is the potential rollover of the + *underlying hardware counter* between `timer_monotonic_get` calls, + which the implementation must handle correctly (typically ok for + intervals up to several seconds). +4. **Busy Waiting:** `stopwatch_wait_until_expired` and the internal + loops of `wait_us`/`wait_ms` (and potentially the fallback in + `udelay`/`mdelay`) perform busy-waits, consuming CPU cycles. Consider + alternatives like interrupt-driven timers or yielding + (`thread_yield_microseconds`) if power consumption or concurrency is + critical. +5. **Callback Queue Size:** The timer callback queue has a fixed size + defined by `CONFIG(TIMER_QUEUE_SIZE)`, limiting the number of pending + callbacks. + + +## Troubleshooting + +Common issues and solutions: + +1. **Inaccurate Timing / Durations are Zero:** + - Verify `CONFIG(HAVE_MONOTONIC_TIMER)` is enabled. + - Check if a platform-specific `init_timer()` is required and being + called. + - Ensure the platform timer frequency is correctly configured. + - Check for potential timer hardware issues or conflicts. + +2. **Timeouts Not Working / `wait_ms` Never Returns > 0:** + - Verify the timeout value is appropriate (not too short). + - Double-check the `condition` logic in `wait_ms`/`wait_us`. Is it + actually capable of becoming true? + - Ensure the stopwatch (`sw` or the internal one in `wait_ms`) is + properly initialized before the check loop. + - Confirm `CONFIG(HAVE_MONOTONIC_TIMER)` is enabled. + +3. **Timer Callbacks Not Running:** + - Verify `CONFIG(TIMER_QUEUE)` is enabled. + - Ensure `timer_sched_callback()` returned success (0). + - **Crucially:** Check that `timers_run()` is being called + periodically in a suitable loop after the callback was scheduled. + - Make sure the `struct timeout_callback` structure persists in memory + until the callback runs. + +4. **Performance Impact:** + - Reduce frequency of stopwatch checks or `wait_ms`/`wait_us` calls if + possible. + - Measure larger code blocks. + - Investigate if the underlying `udelay` implementation is + busy-waiting excessively; yielding (`thread_yield_microseconds`) + might be preferable if available and appropriate. + + +## Platform-Specific Considerations + +The core APIs are generic, but the underlying implementation relies on +platform code: +- **Timer Source:** Platforms implement `timer_monotonic_get` (often + weakly linked) or provide the necessary hooks for it, using hardware + like the APIC timer (x86), Time Base (PPC), architectural timers + (ARM), etc. +- **`init_timer()`:** Platforms may need a custom `init_timer` to set up + clocks, dividers, or enable the timer hardware. +- **`udelay`:** Platforms might provide optimized `udelay` + implementations. + +Refer to the documentation and code within specific `src/soc/`, +`src/cpu/`, or `src/arch/` directories for platform details. + + +## References + +- `src/include/timer.h`: Monotonic timer, stopwatch, and callback + declarations. +- `src/include/delay.h`: `udelay`, `mdelay`, `delay` declarations. +- `src/lib/timer.c`: Generic `udelay` (using stopwatch/yield), weak + `init_timer`. +- `src/lib/delay.c`: Generic `mdelay` and `delay` implementations + (calling `udelay`). +- `src/lib/hardwaremain.c`: Example usage of `timers_run()` in boot + state machine. +- Platform timer implementations: Search for `timer_monotonic_get` or + `init_timer` in `src/cpu/`, `src/soc/`, `src/arch/` directories + (e.g., `src/cpu/x86/lapic/apic_timer.c`, + `src/arch/arm64/timer.c`).