@@ -692,6 +692,7 @@ extern int hal_get_signal_value_by_name(
692692extern 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);
983984extern 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+
9861078RTAPI_END_DECLS
9871079
9881080#endif /* HAL_H */
0 commit comments