summaryrefslogtreecommitdiff
path: root/mbglib/common/chk_time_info.c
blob: bb8cedca39d8fb5c03427065ef172cc04545117a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287

/**************************************************************************
 *
 *  $Id: chk_time_info.c 1.10 2021/04/29 14:20:17 martin REL_M $
 *
 *  Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
 *
 *  Description:
 *    System time checking support functions.
 *
 * -----------------------------------------------------------------------
 *  $Log: chk_time_info.c $
 *  Revision 1.10  2021/04/29 14:20:17  martin
 *  Variable pc_cycles_frequency was renamed to mbg_pc_cycles_frequency.
 *  Revision 1.9  2021/04/12 22:16:34  martin
 *  New function snprint_chk_time_dev_name().
 *  Renamed some local variables and updated comments.
 *  Revision 1.8  2021/04/07 18:09:30  martin
 *  Moved postprocessing of raw data pairs to a separate
 *  function, mbg_proc_chk_time_info().
 *  Revision 1.7  2021/03/21 18:04:47  martin
 *  Updated some comments.
 *  Revision 1.6  2021/03/12 14:20:18  martin
 *  Use the new global variable pc_cycles_frequency.
 *  Revision 1.5  2019/02/08 10:13:17  martin
 *  Made code more portable.
 *  Account for renamed symbols.
 *  Added some comments.
 *  Revision 1.4  2017/07/05 09:00:14  martin
 *  Use safe string functions from str_util.c.
 *  Account for PCPS_HRT_BIN_FRAC_SCALE renamed
 *  to MBG_FRAC32_UNITS_PER_SEC.
 *  Revision 1.3  2013/07/30 12:55:33  martin
 *  Updated file description.
 *  Revision 1.2  2013/03/04 16:01:01  martin
 *  Use common function setup_hr_time_cycles_from_timestamp_cycles().
 *  Made snprint_chk_time_info() more flexible.
 *  Revision 1.1  2012/05/29 09:52:26  martin
 *  Initial revision.
 *
 **************************************************************************/

#define _CHK_TIME_INFO
  #include <chk_time_info.h>
#undef _CHK_TIME_INFO

#include <str_util.h>
#include <toolutil.h>  // Common utility functions.

#include <stdio.h>



static /*HDR*/
/**
 * @brief Compute the mean value of a number of cycles.
 *
 * @param[in,out]  p_filter  Address of a ::CYCLES_FILTER_DATA structure that needs to be updated.
 * @param[in]      cyc       The last recent cycles value to be taken into account.
 *
 * @return  The mean cycles value computed from the values in the filter structure @p p.
 */
MBG_PC_CYCLES do_filter( CYCLES_FILTER_DATA *p_filter, MBG_PC_CYCLES cyc )
{
  if ( p_filter->entries < MAX_CYCLES_FILTER_ENTRIES )
    p_filter->entries++;

  if ( ++( p_filter->index ) >= MAX_CYCLES_FILTER_ENTRIES )
    p_filter->index = 0;

  // Update the sum of filter entries.
  p_filter->sum -= p_filter->cyc[p_filter->index];  // Subtract oldest sample.
  p_filter->cyc[p_filter->index] = cyc;             // Save new sample.
  p_filter->sum += cyc;                             // Add new sample.

  return p_filter->sum / p_filter->entries;         // Return mean value.

}  // do_filter



/*HDR*/
/**
 * @brief Process raw system timestamp / reference timestamp pairs.
 *
 * Calculates and fills some fields in a ::MBG_CHK_TIME_INFO structure
 * to simplify further processing by the calling application.
 *
 * @param[in,out]  p_cti     Pointer to a ::MBG_CHK_TIME_INFO structure to be handled.
 * @param[in,out]  p_filter  Optional address of a ::CYCLES_FILTER_DATA instance
 *                           associated with the time stamps. If this parameter is
 *                           not @a NULL, the execution limit is also updated.
 *
 * @see ::mbg_chk_time_info
 */
void mbg_proc_chk_time_info( MBG_CHK_TIME_INFO *p_cti, CYCLES_FILTER_DATA *p_filter )
{
  // Setup some pointers to make subsequent code clearer.
  PCPS_TIME_STAMP *p_ref_ts = &p_cti->hrti.ref_hr_time_cycles.t.tstamp;  // The reference timestamp from the device.
  MBG_PC_CYCLES *p_ref_cyc = &p_cti->hrti.ref_hr_time_cycles.cycles;     // The reference time cycles.
  MBG_SYS_TIME_CYCLES *p_sys_tic = &p_cti->hrti.sys_time_cycles;         // The system time cycles.

  // Convert both timestamps to floating point format.
  p_cti->d_sys = (double) p_sys_tic->sys_time.secs + ( (double) p_sys_tic->sys_time.nano_secs / NSEC_PER_SEC );
  p_cti->d_ref = (double) p_ref_ts->sec + ( ( (double) p_ref_ts->frac ) / (double) MBG_FRAC32_UNITS_PER_SEC );

  // Compute the cycles deltas and limit.
  p_cti->ltcy_cyc = mbg_delta_pc_cycles( p_ref_cyc, &p_sys_tic->cyc_after );
  p_cti->exec_cyc = mbg_delta_pc_cycles( &p_sys_tic->cyc_after, &p_sys_tic->cyc_before );
  p_cti->exec_cyc_limit = p_filter ? do_filter( p_filter, p_cti->exec_cyc ) : 0;

  if ( mbg_pc_cycles_frequency )
  {
    // If the cycles frequency is known, also
    // compute the cycles deltas and limit as [s].
    p_cti->ltcy_sec = ( (double) p_cti->ltcy_cyc ) / (double) mbg_pc_cycles_frequency;
    p_cti->exec_sec = ( (double) p_cti->exec_cyc ) / (double) mbg_pc_cycles_frequency;
    p_cti->exec_sec_limit = ( (double) p_cti->exec_cyc_limit ) / (double) mbg_pc_cycles_frequency;
  }

  // The system timestamp and device timestamp have been read
  // after each other, with associated cycles numbers.
  // Use the delta cycles to normalize the device timestamp
  // and relate it to the system timestamp.
  p_cti->d_ref_comp = p_cti->d_ref - p_cti->ltcy_sec;

}  // mbg_proc_chk_time_info



/*HDR*/
/**
 * @brief Read and evaluate a system timestamp / reference timestamp pair.
 *
 * The device timestamp is considered as reference timestamp.
 *
 * Usually the @p fast_ts_only flag should be 0, in which case a ::PCPS_HR_TIME
 * is read from the device in conjunction with the system timestamp.
 *
 * If the @p fast_ts_only flag is not 0, only a ::PCPS_TIME_STAMP instead of a
 * ::PCPS_HR_TIME is read in conjunction with the system timestamp. In this case
 * the call executes faster, but this is only supported if the device supports
 * memory mapped timestamps (see ::chk_fast_tstamp_supp). Also, the returned data
 * doesn't include the device status information in this case.
 *
 * Once the device system timestamp / reference timestamp pair has been read
 * successfully, ::mbg_proc_chk_time_info is called to evaluate the associated
 * cycles values to simplify further processing by the calling application.
 *
 * @param[in]      dh        Valid ::MBG_DEV_HANDLE handle to a Meinberg device.
 * @param[out]     p_cti     Pointer to a ::MBG_CHK_TIME_INFO structure to be written.
 * @param[in,out]  p_filter  Optional address of a ::CYCLES_FILTER_DATA instance
 *                           associated with the device that is referenced by @p dh.
 *                           If this parameter is not @a NULL, the execution limit is
 *                           automatically updated.
 * @param[in]  fast_ts_only  A flag indicating that only a ::PCPS_TIME_STAMP
 *                           instead of a ::PCPS_HR_TIME is to be read
 *                           in conjunction with the system timestamp,
 *                           usually 0.
 *
 * @return ::MBG_SUCCESS on success, else one of the @ref MBG_ERROR_CODES.
 *
 * @see ::mbg_proc_chk_time_info
 * @see ::snprint_chk_time_info
 * @see ::mbg_get_time_info_hrt
 * @see ::mbg_get_time_info_tstamp
 */
int mbg_chk_time_info( MBG_DEV_HANDLE dh, MBG_CHK_TIME_INFO *p_cti, CYCLES_FILTER_DATA *p_filter, int fast_ts_only )
{
  MBG_PC_CYCLES tmp;
  int rc;

  memset( p_cti, 0, sizeof( *p_cti ) );

  if ( fast_ts_only )
  {
    // We have to read the current system time and the timestamp
    // as ::PCPS_TIME_STAMP_CYCLES from a device.
    MBG_TIME_INFO_TSTAMP tsi;

    rc = mbg_get_time_info_tstamp( dh, &tsi );

    // For subsequent processing we have to convert this to the same
    // data format as used if the flag 'fast_ts_only' is not set.
    setup_hr_time_cycles_from_timestamp_cycles( &p_cti->hrti.ref_hr_time_cycles, &tsi.ref_tstamp_cycles );
    p_cti->hrti.sys_time_cycles = tsi.sys_time_cycles;
  }
  else
    rc = mbg_get_time_info_hrt( dh, &p_cti->hrti );

  if ( mbg_rc_is_error( rc ) )
    return rc;

  // Do some common processing.
  mbg_proc_chk_time_info( p_cti, p_filter );

  // Try to set the limit to 1.7 of the mean execution cycles.
  tmp = ( 7 * p_cti->exec_cyc_limit ) / 10;

  // If execution takes only a few cycles, make sure the limit
  // is above the mean number of cycles.
  if ( tmp == 0 )
    tmp++;

  p_cti->exec_cyc_limit += tmp;

  return MBG_SUCCESS;

}  // mbg_chk_time_info



/*HDR*/
/**
 * @brief Print a device name into a string buffer.
 *
 * Format according to ::snprint_chk_time_info.
 *
 * @param[out] s         The string buffer to be filled.
 * @param[in]  max_len   Size of the output buffer for 0-terminated string.
 * @param[in]  dev_name  Pointer to a ::MBG_CHK_TIME_INFO to be evaluated.
 *
 * @return Length of the string in the buffer.
 *
 * @see ::mbg_chk_time_info
 */
int snprint_chk_time_dev_name( char *s, size_t max_len, const char *dev_name )
{
  return snprintf_safe( s, max_len, "%-9s: ", dev_name );

}  // snprint_chk_time_dev_name



/*HDR*/
/**
 * @brief Print info from a ::MBG_CHK_TIME_INFO structure into a string buffer.
 *
 * @param[out] s            The string buffer to be filled.
 * @param[in]  max_len      Size of the output buffer for 0-terminated string.
 * @param[in]  p_cti        Pointer to a ::MBG_CHK_TIME_INFO to be evaluated.
 * @param[in]  p_dev        Pointer to a device info, optional, may be @a NULL.
 * @param[in]  frac_digits  Number of fractional digits to be printed, e.g. 9 for nanoseconds.
 * @param[in]  print_raw    If not 0, raw values are also printed.
 *
 * @return Length of the string in the buffer.
 *
 * @see ::mbg_chk_time_info
 */
int snprint_chk_time_info( char *s, size_t max_len, const MBG_CHK_TIME_INFO *p_cti, const PCPS_DEV *p_dev,
                           int frac_digits, int print_raw )
{
  size_t n = 0;

  if ( p_dev )
    n += snprint_chk_time_dev_name( &s[n], max_len - n, _pcps_type_name( p_dev ) );

  n += mbg_snprint_hr_tstamp( &s[n], max_len - n, &p_cti->hrti.ref_hr_time_cycles.t.tstamp, 0, 0 );  // Raw timestamp?

  n += snprintf_safe( &s[n], max_len - n, " %.*f %.*f ",
                      frac_digits, p_cti->d_ref,
                      frac_digits, p_cti->d_sys );

  n += snprintf_safe( &s[n], max_len - n, "%+.*f, ltcy: ",
                      frac_digits, p_cti->d_ref_comp - p_cti->d_sys );


  if ( mbg_pc_cycles_frequency )  // Print latency and execution time in microseconds.
  {
    n += snprintf_safe( &s[n], max_len - n, "%.2f us, exec: %.2f us, limit: %.2f us",
                        p_cti->ltcy_sec * 1e6, p_cti->exec_sec * 1e6, p_cti->exec_sec_limit * 1e6 );
  }
  else  // Print latency and execution time in cycles only.
  {
    n += snprintf_safe( &s[n], max_len - n, "%" PRIi64 " cyc, exec: %" PRIi64 " cyc, limit: %" PRIi64 " cyc",
                        (int64_t) p_cti->ltcy_cyc, (int64_t) p_cti->exec_cyc,
                        (int64_t) p_cti->exec_cyc_limit );
  }

  if ( print_raw )
    n += snprintf_safe( &s[n], max_len - n, ", raw: %+.*f",
                        frac_digits, p_cti->d_ref - p_cti->d_sys );

  return _int_from_size_t( n );

}  // snprint_chk_time_info