File syn_hpclock.c¶
File List > drivers > syn_hpclock.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_HPCLOCK) || SYN_USE_HPCLOCK
#include "../util/syn_assert.h"
#include "syn_hpclock.h"
/* ── Overflow counter (shared with port ISR) ───────────────────────────── */
volatile uint32_t syn_hpclock_msb;
/* ── Resolution ────────────────────────────────────────────────────────── */
uint64_t syn_hpclock_resolve(const SYN_HPTimestamp *ts)
{
if (ts == NULL)
return 0;
SYN_ASSERT(ts != NULL);
/*
* Three reads: msb_1, lsb, msb_2.
*
* The timer overflow ISR (highest priority) can preempt at any
* point during the capture. Two cases:
*
* msb_1 == msb_2:
* No overflow during the capture window. Use either MSB.
*
* msb_1 != msb_2:
* Overflow occurred during the window. The hardware counter
* wrapped from 0xFFFFFFFF to 0x00000000 at some point between
* the msb_1 and msb_2 reads. The LSB value tells us which
* side of the wrap it was captured on:
*
* - lsb < 0x80000000: The counter already wrapped before the
* LSB read. lsb is a small post-wrap value. Use msb_2
* (the post-overflow count).
*
* - lsb >= 0x80000000: The counter had not yet wrapped when
* the LSB was read. lsb is a large pre-wrap value. Use
* msb_1 (the pre-overflow count).
*
* Safety of the half-range check: the three reads complete in
* ~10 CPU cycles. Half a 32-bit counter period at any practical
* system clock (≥ 1 MHz) is > 2000 seconds. The LSB cannot
* advance through half the range during the capture window.
*/
uint32_t msb;
if (ts->msb_1 == ts->msb_2) {
/* No overflow in the capture window. */
msb = ts->msb_1;
} else {
/* Overflow occurred — determine which side of the wrap. */
msb = (ts->lsb < 0x80000000u) ? ts->msb_2 /* post-wrap */
: ts->msb_1; /* pre-wrap */
}
return ((uint64_t)msb << 32) | (uint64_t)ts->lsb;
}
/* ── Conversion ────────────────────────────────────────────────────────── */
uint64_t syn_hpclock_ticks_to_ns(uint64_t ticks)
{
uint32_t freq_hz = syn_port_hpclock_freq_hz();
if (freq_hz == 0)
return 0;
/*
* ns = ticks * 1,000,000,000 / freq_hz
*
* To avoid overflow in the multiply, split into whole seconds
* and remainder ticks:
* whole_s = ticks / freq_hz
* rem_ticks = ticks % freq_hz
* ns = whole_s * 1,000,000,000 + rem_ticks * 1,000,000,000 / freq_hz
*
* The remainder multiply fits in 64 bits as long as
* rem_ticks < freq_hz and freq_hz < 4.29 GHz (always true for
* any practical MCU clock).
*/
uint64_t whole_s = ticks / freq_hz;
uint64_t rem_ticks = ticks % freq_hz;
return whole_s * 1000000000ULL + rem_ticks * 1000000000ULL / freq_hz;
}
/* ── Elapsed ───────────────────────────────────────────────────────────── */
uint64_t syn_hpclock_elapsed(const SYN_HPTimestamp *start, const SYN_HPTimestamp *end)
{
SYN_ASSERT(start != NULL);
SYN_ASSERT(end != NULL);
return syn_hpclock_resolve(end) - syn_hpclock_resolve(start);
}
#endif /* SYN_USE_HPCLOCK */