/ src / hal / utils / scope_rt.c
scope_rt.c
  1  /** This file, 'halscope_rt.c', is a HAL component that together with
  2      'halscope.c' provides an oscilloscope to view HAL pins, signals,
  3      and parameters
  4  */
  5  
  6  /** Copyright (C) 2003 John Kasunich
  7                         <jmkasunich AT users DOT sourceforge DOT net>
  8  */
  9  
 10  /** This program is free software; you can redistribute it and/or
 11      modify it under the terms of version 2 of the GNU General
 12      Public License as published by the Free Software Foundation.
 13      This library is distributed in the hope that it will be useful,
 14      but WITHOUT ANY WARRANTY; without even the implied warranty of
 15      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16      GNU General Public License for more details.
 17  
 18      You should have received a copy of the GNU General Public
 19      License along with this library; if not, write to the Free Software
 20      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 21  
 22      THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR
 23      ANY HARM OR LOSS RESULTING FROM ITS USE.  IT IS _EXTREMELY_ UNWISE
 24      TO RELY ON SOFTWARE ALONE FOR SAFETY.  Any machinery capable of
 25      harming persons must have provisions for completely removing power
 26      from all motors, etc, before persons enter any danger area.  All
 27      machinery must be designed to comply with local and national safety
 28      codes, and the authors of this software can not, and do not, take
 29      any responsibility for such compliance.
 30  
 31      This code was written as part of the EMC HAL project.  For more
 32      information, go to www.linuxcnc.org.
 33  */
 34  
 35  #include <rtapi.h>		/* RTAPI realtime OS API */
 36  #include <rtapi_app.h>		/* RTAPI realtime module decls */
 37  #include <hal.h>		/* HAL public API decls */
 38  #include "../hal_priv.h"	/* HAL private API decls */
 39  #include "scope_rt.h"		/* scope related declarations */
 40  #include "rtapi_string.h"
 41  
 42  /* module information */
 43  MODULE_AUTHOR("John Kasunich");
 44  MODULE_DESCRIPTION("Oscilloscope for EMC HAL");
 45  MODULE_LICENSE("GPL");
 46  
 47  long num_samples = 16000;
 48  long shm_size;
 49  RTAPI_MP_LONG(num_samples, "Number of samples in the shared memory block")
 50  
 51  /***********************************************************************
 52  *                         GLOBAL VARIABLES                             *
 53  ************************************************************************/
 54  
 55  scope_rt_control_t *ctrl_rt;	/* ptr to main RT control structure */
 56  scope_shm_control_t *ctrl_shm;	/* ptr to shared mem control struct */
 57  
 58  /***********************************************************************
 59  *                         LOCAL VARIABLES                              *
 60  ************************************************************************/
 61  
 62  static int comp_id;		/* component ID */
 63  static int shm_id;		/* shared memory ID */
 64  static scope_rt_control_t ctrl_struct;	/* realtime control structure */
 65  
 66  /***********************************************************************
 67  *                  LOCAL FUNCTION DECLARATIONS                         *
 68  ************************************************************************/
 69  
 70  static void init_rt_control_struct(void *shmem);
 71  static void init_shm_control_struct(void);
 72  
 73  static void sample(void *arg, long period);
 74  static void capture_sample(void);
 75  static int check_trigger(void);
 76  
 77  /***********************************************************************
 78  *                       INIT AND EXIT CODE                             *
 79  ************************************************************************/
 80  
 81  int rtapi_app_main(void)
 82  {
 83      int retval;
 84      void *shm_base;
 85      long skip;
 86      /* connect to the HAL */
 87      comp_id = hal_init("scope_rt");
 88      if (comp_id < 0) {
 89  	rtapi_print_msg(RTAPI_MSG_ERR, "SCOPE: ERROR: hal_init() failed\n");
 90  	return -1;
 91      }
 92      /* connect to scope shared memory block */
 93      skip = (sizeof(scope_shm_control_t) + 3) & ~3;
 94      shm_size = skip + num_samples * sizeof(scope_data_t);
 95      shm_id = rtapi_shmem_new(SCOPE_SHM_KEY, comp_id, shm_size);
 96      if (shm_id < 0) {
 97  	rtapi_print_msg(RTAPI_MSG_ERR,
 98  	    "SCOPE RT: ERROR: failed to get shared memory (key=0x%x, size=%lu)\n",
 99              SCOPE_SHM_KEY,
100              shm_size
101          );
102  	hal_exit(comp_id);
103  	return -1;
104      }
105      retval = rtapi_shmem_getptr(shm_id, &shm_base);
106      if (retval < 0) {
107  	rtapi_print_msg(RTAPI_MSG_ERR,
108  	    "SCOPE: ERROR: failed to map shared memory\n");
109  	rtapi_shmem_delete(shm_id, comp_id);
110  	hal_exit(comp_id);
111  	return -1;
112      }
113  
114      /* init control structure */
115      ctrl_rt = &ctrl_struct;
116      init_rt_control_struct(shm_base);
117  
118      /* export scope data sampling function */
119      retval = hal_export_funct("scope.sample", sample, NULL, 0, 0, comp_id);
120      if (retval != 0) {
121  	rtapi_print_msg(RTAPI_MSG_ERR,
122  	    "SCOPE_RT: ERROR: sample funct export failed\n");
123  	hal_exit(comp_id);
124  	return -1;
125      }
126      rtapi_print_msg(RTAPI_MSG_DBG, "SCOPE_RT: installed sample function\n");
127      hal_ready(comp_id);
128      return 0;
129  }
130  
131  void rtapi_app_exit(void)
132  {
133      /* is sample function linked to a thread? */
134      if (ctrl_shm->thread_name[0] != '\0') {
135  	/* need to unlink it before we release the scope shared memory */
136  	hal_del_funct_from_thread("scope.sample", ctrl_shm->thread_name);
137      }
138      rtapi_shmem_delete(shm_id, comp_id);
139      hal_exit(comp_id);
140  }
141  
142  /***********************************************************************
143  *                          REALTIME FUNCTIONS                          *
144  ************************************************************************/
145  
146  static void sample(void *arg, long period)
147  {
148      int n;
149  
150      ctrl_shm->watchdog = 0;
151      if (ctrl_shm->state == RESET) {
152  	/* sampling interrupted, reset everything */
153  	ctrl_shm->curr = 0;
154  	ctrl_shm->start = ctrl_shm->curr;
155  	ctrl_shm->samples = 0;
156  	ctrl_shm->force_trig = 0;
157  	/* reset completed, set new state */
158  	ctrl_shm->state = IDLE;
159      }
160      ctrl_rt->mult_cntr++;
161      if (ctrl_rt->mult_cntr < ctrl_shm->mult) {
162  	/* not time to do anything yet */
163  	return;
164      }
165      /* reset counter */
166      ctrl_rt->mult_cntr = 0;
167      /* run the sampling state machine */
168      switch (ctrl_shm->state) {
169      case IDLE:
170  	/* do nothing while waiting for INIT */
171  	break;
172      case INIT:
173  	/* init start pointer, curr pointer, sample count */
174  	ctrl_shm->curr = 0;
175  	ctrl_shm->start = ctrl_shm->curr;
176  	ctrl_shm->samples = 0;
177  	ctrl_shm->force_trig = 0;
178  	ctrl_rt->auto_timer = 0;
179  	/* get info about channels */
180  	for (n = 0; n < 16; n++) {
181  	    ctrl_rt->data_addr[n] = SHMPTR(ctrl_shm->data_offset[n]);
182  	    ctrl_rt->data_type[n] = ctrl_shm->data_type[n];
183  	    ctrl_rt->data_len[n] = ctrl_shm->data_len[n];
184  	}
185  	/* set next state */
186  	ctrl_shm->state = PRE_TRIG;
187  	break;
188      case PRE_TRIG:
189  	/* acquire a sample */
190  	capture_sample();
191  	/* increment sample counter */
192  	ctrl_shm->samples++;
193  	/* check if all pre-trigger samples captured */
194  	if (ctrl_shm->samples >= ctrl_shm->pre_trig) {
195  	    /* yes - start waiting for trigger */
196  	    ctrl_shm->state = TRIG_WAIT;
197  	    /* dummy call to preset 'compare_result' */
198  	    check_trigger();
199  	}
200  	break;
201      case TRIG_WAIT:
202  	/* acquire a sample */
203  	capture_sample();
204  	/* increment sample counter */
205  	ctrl_shm->samples++;
206  	/* check if trigger condition met */
207  	if (check_trigger()) {
208  	    /* yes - start acquiring post trigger data */
209  	    ctrl_shm->state = POST_TRIG;
210  	} else {
211  	    /* no, discard oldest pre-trig sample */
212  	    ctrl_shm->samples--;
213  	    ctrl_shm->start += ctrl_shm->sample_len;
214  	    /* is there a valid sample here, or end of buffer? */
215  	    if ((ctrl_shm->start + ctrl_shm->sample_len) > ctrl_shm->buf_len) {
216  		/* end of buffer, wrap back to beginning */
217  		ctrl_shm->start = 0;
218  	    }
219  	}
220  	break;
221      case POST_TRIG:
222  	/* acquire a sample */
223  	capture_sample();
224  	/* increment sample counter */
225  	ctrl_shm->samples++;
226  	/* check if all post-trigger samples captured */
227  	if (ctrl_shm->samples >= ctrl_shm->rec_len) {
228  	    /* yes - stop sampling and cleanup */
229  	    ctrl_shm->state = DONE;
230  	}
231  	break;
232      case DONE:
233  	/* do nothing while GUI displays waveform */
234  	break;
235      default:
236  	/* shouldn't get here - if we do, set a legal state */
237  	ctrl_shm->state = IDLE;
238  	break;
239      }
240      /* done */
241  }
242  
243  static void capture_sample(void)
244  {
245      scope_data_t *dest;
246      int n;
247  
248      dest = &(ctrl_rt->buffer[ctrl_shm->curr]);
249      /* loop through all channels to acquire data */
250      for (n = 0; n < 16; n++) {
251  	/* capture 1, 2, or 4 bytes, based on data size */
252  	switch (ctrl_rt->data_len[n]) {
253  	case 1:
254  	    dest->d_u8 = *((unsigned char *) (ctrl_rt->data_addr[n]));
255  	    dest++;
256  	    break;
257  	case 4:
258  	    dest->d_u32 = *((unsigned long *) (ctrl_rt->data_addr[n]));
259  	    dest++;
260  	    break;
261  	case 8:
262              {
263                  ireal_t sample_a, sample_b;
264                  do {
265                      sample_a = *((volatile ireal_t *) (ctrl_rt->data_addr[n]));
266                      sample_b = *((volatile ireal_t *) (ctrl_rt->data_addr[n]));
267                  } while( sample_a != sample_b );
268                  dest->d_ireal = sample_a;
269                  dest++;
270              }
271  	    break;
272  	default:
273  	    break;
274  	}
275      }
276      /* increment sample pointer */
277      ctrl_shm->curr += ctrl_shm->sample_len;
278      /* is there room in the buffer for another sample? */
279      if ((ctrl_shm->curr + ctrl_shm->sample_len) > ctrl_shm->buf_len) {
280  	/* no, wrap back to beginning of buffer */
281  	ctrl_shm->curr = 0;
282      }
283  }
284  
285  // TODO: type-independent way to get high bit
286  // #define SIGN_BIT (~(((ireal_t)~(ireal_t)0)>>1))
287  static int check_trigger(void)
288  {
289      static int compare_result = 0;
290      int prev_compare_result;
291      scope_data_t *value, *level;
292  
293      /* has user forced trigger? */
294      if (ctrl_shm->force_trig != 0) {
295  	return 1;
296      }
297      /* is auto trigger enabled? */
298      if (ctrl_shm->auto_trig != 0) {
299  	/* yes, has the delay time expired? */
300  	if (++ctrl_rt->auto_timer >= ctrl_shm->rec_len) {
301  	    return 1;
302  	}
303      } else {
304  	/* no auto, reset delay timer */
305  	ctrl_rt->auto_timer = 0;
306      }
307      /* if no trigger channel is selected we're done */
308      if (ctrl_shm->trig_chan == 0) {
309  	return 0;
310      }
311      /* point a scope_data_t union at the signal value */
312      value = ctrl_rt->data_addr[ctrl_shm->trig_chan - 1];
313      /* and at the trigger level */
314      level = &(ctrl_shm->trig_level);
315      /* save previous compare result */
316      prev_compare_result = compare_result;
317      /* compare actual value to trigger level */
318      switch (ctrl_rt->data_type[ctrl_shm->trig_chan - 1]) {
319      case HAL_BIT:
320  	/* for bits, we don't even look at the trigger level */
321  	compare_result = value->d_u8;
322  	break;
323      case HAL_FLOAT:
324  	{
325  	ireal_t tmp1, tmp2;
326  	/* don't want to use the FPU in this function, so we use */
327  	/* a hack - see http://en.wikipedia.org/wiki/IEEE_754 */
328  	/* this _only_ works with IEEE-754 floating point numbers */
329  	/* and will probably fail for infinities, NANs, etc. */
330  	/* OK, here we go! */
331  	/* get the value as an integer */
332  	tmp1 = value->d_ireal;
333  	/* get the trigger as an integer */
334  	tmp2 = level->d_ireal;
335  	/* is the value negative? (highest bit) */
336  	if (tmp1 & 0x8000000000000000ull) {
337  	    /* yes, is the trigger level negative? */
338  	    if (tmp2 & 0x8000000000000000ull) {
339  		/* yes, make both positive */
340  		tmp1 ^= 0x8000000000000000ull;
341  		tmp2 ^= 0x8000000000000000ull;
342  		/* and compare them as unsigned ints */
343  		/* because of negation, we reverse the compare */
344  		compare_result = (tmp1 < tmp2);
345  	    } else {
346  		/* trigger level positive, value negative */
347  		compare_result = 0;
348  	    }
349  	} else {
350  	    /* value is positive, is trigger level negative? */
351  	    if (tmp2 & 0x8000000000000000ull) {
352  		/* trigger level negative, value positive */
353  		compare_result = 1;
354  	    } else {
355  		/* both are positive */
356  		/* compare them as unsigned ints */
357  		compare_result = (tmp1 > tmp2);
358  	    }
359  	}
360  	}
361  	break;
362      case HAL_S32:
363  	compare_result = (value->d_s32 > level->d_s32);
364  	break;
365      case HAL_U32:
366  	compare_result = (value->d_u32 > level->d_u32);
367  	break;
368      default:
369  	compare_result = 0;
370  	break;
371      }
372      /* test for rising edge */
373      if (ctrl_shm->trig_edge && compare_result && !prev_compare_result) {
374  	return 1;
375      }
376      /* test for falling edge */
377      if (!ctrl_shm->trig_edge && !compare_result && prev_compare_result) {
378  	return 1;
379      }
380      return 0;
381  }
382  
383  /***********************************************************************
384  *                   LOCAL FUNCTION DEFINITIONS                         *
385  ************************************************************************/
386  
387  static void init_rt_control_struct(void *shmem)
388  {
389      char *cp;
390      int n, skip;
391  
392      /* first clear entire struct to all zeros */
393      cp = (char *) ctrl_rt;
394      for (n = 0; n < sizeof(scope_rt_control_t); n++) {
395  	cp[n] = 0;
396      }
397      /* save pointer to shared control structure */
398      ctrl_shm = shmem;
399      /* round size of shared struct up to a multiple of 4 for alignment */
400      skip = (sizeof(scope_shm_control_t) + 3) & ~3;
401      /* the rest of the shared memory area is the data buffer */
402      ctrl_rt->buffer = (scope_data_t *) (((char *) (shmem)) + skip);
403      init_shm_control_struct();
404      /* init any non-zero fields */
405  
406      /* done */
407  }
408  
409  static void init_shm_control_struct(void)
410  {
411      char *cp;
412      int skip, n;
413  
414      /* first clear entire struct to all zeros */
415      cp = (char *) ctrl_shm;
416      for (n = 0; n < sizeof(scope_shm_control_t); n++) {
417  	cp[n] = 0;
418      }
419      /* round size of shared struct up to a multiple of 4 for alignment */
420      ctrl_shm->shm_size = shm_size;
421      skip = (sizeof(scope_shm_control_t) + 3) & ~3;
422      /* remainder of shmem area is buffer */
423      ctrl_shm->buf_len = (shm_size - skip) / sizeof(scope_data_t);
424      /* init any non-zero fields */
425      ctrl_shm->mult = 1;
426      ctrl_shm->state = IDLE;
427  }