From 4dc0e03def5aeb5e6f57ef7c79cd181a3b98ecdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=B6ftner?= Date: Tue, 14 Apr 2026 17:12:42 +0200 Subject: [PATCH] add hal_extend_int function helper function to deal with wrap around and extension of lower-width counters to 64bit ints. --- src/hal/hal.h | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/hal/hal.h b/src/hal/hal.h index 8c6a674cb8b..00a1ba34465 100644 --- a/src/hal/hal.h +++ b/src/hal/hal.h @@ -692,6 +692,24 @@ extern int hal_get_signal_value_by_name( extern int hal_get_param_value_by_name( const char *name, hal_type_t *type, hal_data_u **data); +/*********************************************************************** +* MISC HELPER FUNCTIONS * +************************************************************************/ + +/** hal_extend_counter() extends a counter value with nbits to 64 bits. + + For some hardware counters it may be desireable to extend their + range beyond their native width. + + This function maintains a 64bit counter value and deals with wrap + arounds. Increments between updates need to be less than 2**(nbits-1). + Call with current 64bit counter value to be updated as @param old, + new lower-bit counter value read from hardware as @param newlow + and width of counter as @param nbits. + @returns new counter 64bit counter value. + Code by Jeff Epler. */ +extern rtapi_s64 hal_extend_int(rtapi_s64 old, rtapi_s64 newlow, int nbits); + /*********************************************************************** * EXECUTION RELATED FUNCTIONS * ************************************************************************/ @@ -983,6 +1001,41 @@ extern bool hal_stream_writable(hal_stream_t *stream); extern void hal_stream_wait_writable(hal_stream_t *stream, sig_atomic_t *stop); #endif + +/*********************************************************************** +* MISC HELPER FUNCTIONS * +************************************************************************/ + +static inline rtapi_s64 hal_extend_counter(rtapi_s64 old, rtapi_s64 newlow, int nbits) +{ + /* Extend low-bit-width counter value to 64bit, counting wrap arounds resp. + "rotations" in additional bits. + + see https://github.com/LinuxCNC/linuxcnc/pull/3932#issuecomment-4239206615 + This code avoids branches and undefined behaviour due to signed overflow + Idea from MicroPython. + + To avoid messy code to sign extend a lower-width value to 64bits, shift + the counter value such that the sign bit gets into the MSB. + Calculate the delta from the stored value in the shifted base. + Shifting back the delta assures the sign is extended properly. + Addition as unsigned avoids signed overflow, which would be undefined + behaviour. + + Attention has to be paied that the magnitude of increments / decrements + of the counter stay within 1<<(nshift-1) between calls to this function, + else the wrap around will be counted in the wrong direction. + + Code contributed by Jeff Epler. + */ + int nshift = 64 - nbits; + rtapi_u64 oldlow_shifted = ((rtapi_u64)old << nshift); + rtapi_u64 newlow_shifted = ((rtapi_u64)newlow << nshift); + rtapi_s64 diff_shifted = newlow_shifted - oldlow_shifted; + return (rtapi_u64)old + (diff_shifted >> nshift); +} + + RTAPI_END_DECLS #endif /* HAL_H */