component latencybins
"""comp utility for scripts/latencyhistogram

Usage:
  Read availablebins pin for the number of bins available.
  Set the maxbinnumber pin for the number of +/- bins.
    Ensure maxbinnumber <= availablebins
    For maxbinnumber = N, the bins are numbered:
       -N ... 0 ... + N bins
    (the -0 bin is not populated)
    (total effective bins = 2*maxbinnumber +1)
  Set nsbinsize pin for the binsize (ns)
  Iterate:
    Set index pin to a bin number: 0 <= index <= maxbinnumber.
    Read check pin and verify that check pin == index pin.
    Read pbinvalue,nbinvalue,pextra,nextra pins.
         (pbinvalue is count for bin = +index)
         (nbinvalue is count for bin = -index)
         (pextra    is count for all bins > maxbinnumber)
         (nextra    is count for all bins < maxbinnumber)

   If index is out of range ( index < 0 or index > maxbinnumber)
   then pbinvalue = nbinvalue = -1.
   The reset pin may be used to restart.
   The latency pin outputs the instantaneous latency.

Maintainers note: hardcoded for MAXBINNUMBER==1000

""";

pin in  s32 maxbinnumber = 1000;  // MAXBINNUMBER
pin in  s32 index;  //use s32 to avoid 0x hex display in hal
pin in  bit reset;
pin in  s32 nsbinsize;

pin out s32 check;
pin out s32 latency;
pin out s32 pbinvalue;
pin out s32 nbinvalue;
pin out s32 pextra;
pin out s32 nextra;

// user may interrogate available bins to determine this compiled-in limit
pin out s32 availablebins = 1000; // MAXBINNUMBER

function _ nofp;
variable __s64  last_timer = 0;
variable hal_s32_t    last_binmax = 0;
variable hal_s32_t    first = 1;
variable hal_s32_t    pbins[1001]; // MAXBINNUMBER+1
variable hal_s32_t    nbins[1001]; // MAXBINNUMBER+1
variable hal_s32_t    binmax = 0;

license "GPL";
;;

__s64     now = rtapi_get_time();
hal_s32_t lat32 = (__s32)(now - last_timer - period);
                //(2^31-1)*1nS = 2.147 seconds max

hal_s32_t i;

last_timer = now;

binmax =  maxbinnumber;
if (binmax > availablebins) binmax = availablebins;
last_binmax = binmax;

if (reset) {first = 1;}

if (   first
     || binmax != last_binmax
     || nsbinsize == 0  // important to avoid divide by zero
  ) {
  first = 0;
  latency = 0;
  pextra = 0; nextra = 0;
  for (i = 0; i <= binmax; i++) {
    pbins(i) = 0; nbins(i) = 0;
  }
} else {
  latency = lat32;
  i = lat32/nsbinsize;
  if (i >= 0) {
     if (i > binmax) {
       pextra++;
     } else {
       pbins(i)++;
     }
  } else {
     i = -i;
     if (i > binmax) {
       nextra++;
     } else {
       nbins(i)++;
     }
  }
}

check = index; // user should verify check==index for reading values
// -1 value indicates illegal index
if (index < 0) {
  pbinvalue = -1;
  nbinvalue = -1;
} else if (index <=binmax) {
  pbinvalue = pbins(index);
  nbinvalue = nbins(index);
} else {
  pbinvalue = -1;
  nbinvalue = -1;
}

return 0;
