Skip to content

Commit 8c2f449

Browse files
author
Robert Schöftner
committed
add hal_extend_int function
helper function to deal with wrap around and extension of lower-width counters to 64bit ints.
1 parent fcd8ed1 commit 8c2f449

1 file changed

Lines changed: 92 additions & 0 deletions

File tree

src/hal/hal.h

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@ extern int hal_get_signal_value_by_name(
692692
extern int hal_get_param_value_by_name(
693693
const char *name, hal_type_t *type, hal_data_u **data);
694694

695+
695696
/***********************************************************************
696697
* EXECUTION RELATED FUNCTIONS *
697698
************************************************************************/
@@ -983,6 +984,97 @@ extern bool hal_stream_writable(hal_stream_t *stream);
983984
extern void hal_stream_wait_writable(hal_stream_t *stream, sig_atomic_t *stop);
984985
#endif
985986

987+
988+
/***********************************************************************
989+
* MISC HELPER FUNCTIONS *
990+
************************************************************************/
991+
992+
993+
/** HAL_STATIC_ASSERT wrapper for compile time asserts
994+
*/
995+
#if __STDC_VERSION__ >= 202311L || __cplusplus
996+
#define HAL_STATIC_ASSERT(expression, message) static_assert((expression), message)
997+
#else
998+
/* _Static_assert: GCC extension, in C standard since C11, deprecated in favour of
999+
static_assert (like C++) from C23 onwards*/
1000+
#define HAL_STATIC_ASSERT(expression, message) _Static_assert((expression), message)
1001+
#endif
1002+
1003+
1004+
/** hal_extend_counter() extends a counter value with nbits to 64 bits.
1005+
1006+
For some hardware counters or encoders it may be desireable to
1007+
extend their range beyond their native width.
1008+
1009+
This function maintains a 64bit counter value and counts wrap
1010+
arounds. It may be useful to e.g. keep track of full rotations on
1011+
a gray disc absolute encoder.
1012+
1013+
Changes of hardware encoder between calls to this function need to
1014+
be less than 2**(nbits-1).
1015+
1016+
Usage:
1017+
1018+
Call with current 64bit counter value to be updated as @param old,
1019+
new low-width counter value read from hardware as @param newlow
1020+
and width of counter as @param nbits.
1021+
@returns new counter 64bit counter value which should be stored and
1022+
supplied as old in the next call.
1023+
*/
1024+
static inline rtapi_s64 hal_extend_counter(rtapi_s64 old, rtapi_s64 newlow, int nbits)
1025+
{
1026+
/* Extend low-bit-width counter value to 64bit, counting wrap arounds resp.
1027+
"rotations" in additional bits.
1028+
1029+
see https://github.com/LinuxCNC/linuxcnc/pull/3932#issuecomment-4239206615
1030+
This code avoids branches and undefined behaviour due to signed overflow.
1031+
Idea from MicroPython.
1032+
1033+
To avoid messy code to sign extend and overflow a lower-width value to
1034+
64bits, this code shifts the counter value to the end of a 64bit int.
1035+
Thus the calculated delta value overflows / truncates correctly and gets
1036+
the correct sign.
1037+
1038+
Shifting back the delta assures the sign is extended properly.
1039+
Addition as unsigned avoids signed overflow, which would be undefined
1040+
behaviour.
1041+
1042+
Attention has to be paid that the magnitude of increments / decrements
1043+
of the counter stay within 1<<(nshift-1) between calls to this function,
1044+
else the wrap around will be counted in the wrong direction.
1045+
1046+
Heuristically, if your encoder can do more than half a rotation between
1047+
calls to this function, it is impossible to deduce which direction it
1048+
went.
1049+
1050+
Code contributed by Jeff Epler.
1051+
1052+
Example with 3bit absolute hardware encoder and 8bit extended counter
1053+
(nshift would be 5):
1054+
1055+
counter at 7, transition from 111 (7) to 000 (0) should increment the
1056+
extended counter to 8.
1057+
1058+
call hal_extend_counter(7, 0, 3)
1059+
oldlow_shifted = 7<<5 = 224 (1110 0000)
1060+
newlow_shifted = 0<<5 = 0
1061+
delta_shifted = 0 - 224 = -224 (1 0010 0000) = 32 (0010 0000) truncated to 8 bits
1062+
old + (32 >> 5) = 7 + 1 = 8
1063+
*/
1064+
1065+
/* tripwire if somebody tries to use this code on a Cray with wrong
1066+
compiler flags */
1067+
HAL_STATIC_ASSERT((-2 >> 1) == -1,
1068+
"hal_extend_counter impl only works with arithmetic right shift");
1069+
1070+
int nshift = 64 - nbits;
1071+
rtapi_u64 oldlow_shifted = ((rtapi_u64)old << nshift);
1072+
rtapi_u64 newlow_shifted = ((rtapi_u64)newlow << nshift);
1073+
rtapi_s64 diff_shifted = newlow_shifted - oldlow_shifted;
1074+
return (rtapi_u64)old + (diff_shifted >> nshift);
1075+
}
1076+
1077+
9861078
RTAPI_END_DECLS
9871079

9881080
#endif /* HAL_H */

0 commit comments

Comments
 (0)