@@ -692,6 +692,24 @@ 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+ /***********************************************************************
696+ * MISC HELPER FUNCTIONS *
697+ ************************************************************************/
698+
699+ /** hal_extend_counter() extends a counter value with nbits to 64 bits.
700+
701+ For some hardware counters it may be desireable to extend their
702+ range beyond their native width.
703+
704+ This function maintains a 64bit counter value and deals with wrap
705+ arounds. Increments between updates need to be less than 2**(nbits-1).
706+ Call with current 64bit counter value to be updated as @param old,
707+ new lower-bit counter value read from hardware as @param newlow
708+ and width of counter as @param nbits.
709+ @returns new counter 64bit counter value.
710+ Code by Jeff Epler. */
711+ extern rtapi_s64 hal_extend_int (rtapi_s64 old , rtapi_s64 newlow , int nbits );
712+
695713/***********************************************************************
696714* EXECUTION RELATED FUNCTIONS *
697715************************************************************************/
@@ -983,6 +1001,41 @@ extern bool hal_stream_writable(hal_stream_t *stream);
9831001extern void hal_stream_wait_writable (hal_stream_t * stream , sig_atomic_t * stop );
9841002#endif
9851003
1004+
1005+ /***********************************************************************
1006+ * MISC HELPER FUNCTIONS *
1007+ ************************************************************************/
1008+
1009+ static inline rtapi_s64 hal_extend_counter (rtapi_s64 old , rtapi_s64 newlow , int nbits )
1010+ {
1011+ /* Extend low-bit-width counter value to 64bit, counting wrap arounds resp.
1012+ "rotations" in additional bits.
1013+
1014+ see https://github.com/LinuxCNC/linuxcnc/pull/3932#issuecomment-4239206615
1015+ This code avoids branches and undefined behaviour due to signed overflow
1016+ Idea from MicroPython.
1017+
1018+ To avoid messy code to sign extend a lower-width value to 64bits, shift
1019+ the counter value such that the sign bit gets into the MSB.
1020+ Calculate the delta from the stored value in the shifted base.
1021+ Shifting back the delta assures the sign is extended properly.
1022+ Addition as unsigned avoids signed overflow, which would be undefined
1023+ behaviour.
1024+
1025+ Attention has to be paied that the magnitude of increments / decrements
1026+ of the counter stay within 1<<(nshift-1) between calls to this function,
1027+ else the wrap around will be counted in the wrong direction.
1028+
1029+ Code contributed by Jeff Epler.
1030+ */
1031+ int nshift = 64 - nbits ;
1032+ rtapi_u64 oldlow_shifted = ((rtapi_u64 )old << nshift );
1033+ rtapi_u64 newlow_shifted = ((rtapi_u64 )newlow << nshift );
1034+ rtapi_s64 diff_shifted = newlow_shifted - oldlow_shifted ;
1035+ return (rtapi_u64 )old + (diff_shifted >> nshift );
1036+ }
1037+
1038+
9861039RTAPI_END_DECLS
9871040
9881041#endif /* HAL_H */
0 commit comments