TM
HLS
Block Preview

Introduction

Principle of Operation

ISIS PSD TM is a Vitis HLS core (isispsd.cpp) generated and synthesised by the compiler for every distinct parameter set. It is a time-multiplexed block: the DATA pin carries TM = 4 samples of 16 bit in one 64-bit word per clock, so on the NI DAQ121 (acquisition clock CLK_ACQ = 250 MHz) the block processes 1 Gsample/s.

The block splits the stream in two paths:

  • a trigger path that works on the average of the four samples of the clock, turns it into a rectangular discriminator pulse and shapes that pulse with two cascaded IIR filters. The width of the shaped pulse is what discriminates one particle species from another (the classic pulse shape discrimination);
  • an integration path that works on the raw four lanes after a programmable delay, so that the charge integration can start before the sample that fired the trigger.
   DATA[63:0]  = lane3 | lane2 | lane1 | lane0      (lane0 = bits 15..0 = oldest sample)
      |
      |--> per lane:  x_i = lane_i - OFFSET , negated when POLARITY = 1
      |      |
      |      +--> data16 = (x0+x1+x2+x3) >> 2  ---> compare with TH ---> MON_DISCR
      |                                                    |
      |                                     low-pass IIR --+--> MON_LP
      |                                                    |
      |                                     high-pass IIR -+--> MON_HP --> compare TH_PSD
      |                                                                          |
      |                                          OVR_SAMPLES consecutive clocks --+
      |                                                                          |
      +--> 128-word delay line (PRETRG clocks) --> d0..d3 ---------------> integrate
  

The energy of an event is the sum of the four delayed lanes accumulated over INT_SP clocks:

$$ \mathrm{EVNT_INT} ;=; \sum_{n=0}^{\mathrm{INT_SP}-1};\sum_{i=0}^{3} d_i[n] $$

and the peak is the largest single sample seen inside that window:

$$ \mathrm{EVNT_PEAK} ;=; \max_{n,i}; d_i[n] $$

Because INT_SP and PRETRG are counted in clock cycles, every unit is worth TM = 4 samples (4 ns at 250 MHz).

Pin Description

DATA Input 16 × TM bit TM
Time-multiplexed sample bus – 4 lanes × 16 bit packed into one 64-bit word per clock. Lane 0 sits in bits 15:0 and is the oldest sample of the clock. Unlike every other input, no default value is emitted for it: leaving it open gives the core an unconnected data port.
OFFSET Input 16 bit BIT VECTOR
Pedestal subtracted from every lane before any processing. Applied to both the trigger path and the delayed integration path. Default when left open: x"0000".
Default: 0
POLARITY Input 1 bit BIT
Polarity. When 1 each lane is negated after the offset subtraction (x = -(lane - OFFSET)), which turns negative-going pulses into positive ones. Default when left open: '0'.
Default: 0
TH Input 16 bit BIT VECTOR
Discriminator threshold, compared against the mean of the four samples of the clock. Above it the discriminator emits the constant 6553; below it, 0. Treated as constant (ap_stable). Default when left open: x"01FF" (511).
TH_PSD Input 16 bit BIT VECTOR
PSD threshold applied to the shaped (high-pass) signal MON_HP. Crossing it raises FLAG_TH_PSD and starts the over-threshold counter. Treated as constant (ap_stable). Default when left open: x"000F" (15).
OVR_SAMPLES Input 16 bit BIT VECTOR
Number of consecutive clocks the shaped signal must stay above TH_PSD before an event is armed; the comparison is strict (count > OVR_SAMPLES), and a single clock below TH_PSD resets the count. One unit = 4 input samples. Treated as constant (ap_stable). Default when left open: x"000A" (10).
PRETRG Input 16 bit BIT VECTOR
Pre-trigger delay in clocks applied to the integration path, so the integral starts before the sample that produced the trigger. One unit = 4 input samples. Also added (×4) to the event timestamp. Must stay within 0 … 127 — the delay line has 128 entries. Treated as constant (ap_stable). Default when left open: x"000A" (10).
INT_SP Input 16 bit BIT VECTOR
Integration length in clocks. Each clock adds the four delayed lanes to the accumulator, so the gate covers 4 × INT_SP samples. Treated as constant (ap_stable). Default when left open: x"00A0" (160).
T0 Input 1 bit BIT
Timestamp reset, level sensitive: while high the internal sample counter is held at 0; when low it advances by 4 every clock. Default when left open: '0'.
Default: 0
RESET Input 1 bit BIT
Core reset (ap_rst of the HLS IP). Left open it is tied to the project global reset.
Default: Default Board Reset
MON_DATA Output 16 bit BIT VECTOR
Lane 0 of the delayed data after offset subtraction and polarity inversion — the signal the integrator actually sees.
MON_DISCR Output 16 bit BIT VECTOR
Discriminator output: 6553 while the lane average is above TH, otherwise 0.
MON_LP Output 16 bit BIT VECTOR
Output of the low-pass IIR section fed by the discriminator pulse.
MON_HP Output 16 bit BIT VECTOR
Output of the high-pass IIR section — the signal compared against TH_PSD.
MON_INT Output 32 bit BIT VECTOR
Running charge accumulator of integrator slot 0 only. Not an event value.
MON_PEAK Output 16 bit BIT VECTOR
Running peak of integrator slot 0 only. Not an event value.
FLAG_TH_PSD Output 1 bit BIT
High while the shaped signal MON_HP is above TH_PSD (level).
FLAG_THN_PSD Output 1 bit BIT
One-clock pulse emitted when the over-threshold condition has lasted longer than OVR_SAMPLES and a new event is armed.
FLAG_INT Output 16 bit BIT VECTOR
Bitmap of the four integrators: bit i is high while slot i is integrating. Only bits 3:0 are used.
FLAG_PEAK Output 16 bit BIT VECTOR
Bitmap of the four integrators: bit i is high while slot i is still peak-running, i.e. has not been interrupted by a pile-up. Only bits 3:0 are used.
EVNT_VALID Output 1 bit BIT
High for one clock when a completed event is presented on the EVNT_* pins. At most one event per clock is emitted; slots are drained in the order 0, 1, 2, 3.
EVNT_TS Output 32 bit BIT VECTOR
Event timestamp in samples, latched when the event was armed: EVNT_TS = timestamp + 4 × PRETRG. The internal counter is cleared by T0.
EVNT_INT Output 32 bit BIT VECTOR
Charge integral: the sum of the four delayed lanes accumulated over INT_SP clocks.
EVNT_PEAK Output 16 bit BIT VECTOR
Largest single delayed sample seen inside the integration window.
EVNT_RATIO Output 24 bit BIT VECTOR
Internal mode: (EVNT_INT << 8) / EVNT_PEAK — the integral/peak ratio in Q8. External mode: the constant 1 (do the division outside the block).

Properties

Property window

ADC BITS adcbits

Set the number of raal bits of the board ADC

Nominal number of ADC bits. It does not change the generated core — the value only enters the MD5 that names the HLS IP, so changing it re-synthesises an identical core. Values: 12, 13, 14. Default: 12.

Default: 12

Options: 12 13 14

Time Mux TimeMultiplexing

Set number of samples for each clock cycle

Number of samples carried per clock on DATA. The core is hard-wired to four lanes (64-bit data port, timestamp step of 4, four-term accumulator) and the combo therefore offers a single value. Changing it rebuilds the symbol. Default: 4.

Default: 4

Options: 4

Ratio Ratio

Set if make division internal or external

Where the integral/peak division is performed.

Value Effect
External EVNT_RATIO is the constant 1; declared block latency 20 clocks
Internal EVNT_RATIO = (EVNT_INT << 8) / EVNT_PEAK; declared block latency 55 clocks

The value is part of the HLS IP name, so both variants can coexist in one project. Changing it rebuilds the symbol. Default: External.

Default: External

Options: External Internal

Pileup Pileup

Set if stop integral on pileup or use a secondary integrator to process events

What happens to an integration that is still running when a new event is armed.

Value Effect
Stop Integral the older integration is terminated at once and committed early
Continue the older integration runs to the full INT_SP

The property is not part of the HLS IP name, so two blocks that differ only here share one IP. Its shipped default (External) is not a valid combo value, and with it the core behaves as Continue. Default: External (behaves as Continue).

Default: External

Options: Stop Integral Continue

⚙️ Detailed Operation

Lane unpacking and the TM factor

The symbol pin DATA is declared TYPE_STD_LOGIC_VECTOR_TM, 16 bit wide, with TimeMultiplexed = 4; the HDL pad that is actually generated is a plain 64-bit port named data. Following the repo-wide TM convention, lane 0 lives in the low bits and is the oldest sample of the clock. The core unpacks it as

c
  datax_0 = (data >>  0) & 0xFFFF;   // oldest
datax_1 = (data >> 16) & 0xFFFF;
datax_2 = (data >> 32) & 0xFFFF;
datax_3 = (data >> 48) & 0xFFFF;   // newest
  

Each lane has OFFSET subtracted and is negated when POLARITY = 1 (x = -(lane - OFFSET)), so OFFSET is the pedestal of the digitiser and POLARITY makes negative-going pulses positive.

The Time Mux property only offers the value 4 — the core is hard-wired to four lanes (data is 64 bit, the timestamp advances by 4 per clock, the integrator adds four terms).


Trigger path

  1. Lane average. data16 = (x0 + x1 + x2 + x3) >> 2 — the mean of the four samples of the clock. This is the only signal compared against TH; the block therefore triggers on a 250 MHz decimated version of the 1 GS/s stream.

  2. Discriminator. MON_DISCR is a rectangular pulse: data16 > TH ? 6553 : 0, where 6553 = (int)(0.2 × 0x7FFF). Its width carries the pulse-shape information.

  3. Low-pass section — a 2nd-order IIR written in scattered look-ahead form, which is what lets Vitis close II = 1 at 4 ns:

    $$ y[n] ;=; \sum_{k=0}^{6} b_k,x[n-k] ;-; a_1,y[n-3] ;-; a_2,y[n-6] $$

    with the coefficients compiled into isispsd.cpp:

    coefficient value
    $a_1$ -1.691126416184245
    $a_2$ 0.7213785295739619
    $b_0 \dots b_6$ 0.00093575, 0.00364298, 0.00699309, 0.00838898, 0.00644455, 0.00309409, 0.00075267

    The result is exported on MON_LP.

  4. High-pass section — same idea, 4 feed-forward taps and one feedback tap at $n-3$:

    $$ y[n] ;=; \sum_{k=0}^{3} b_k,x[n-k] ;-; a_1,y[n-3] $$

    coefficient value
    $a_1$ -0.8599680957679054
    $b_0 \dots b_3$ 0.97547839, -0.0478406, -0.04549434, -0.88214345

    The result is exported on MON_HP.

    Coefficients are stored as ap_fixed<18,2> (2 integer bits, 16 fractional bits) and the filter states as ap_fixed<24,16>.

  5. PSD decision. While MON_HP > TH_PSD, FLAG_TH_PSD is high and an internal counter runs. When that counter becomes strictly greater than OVR_SAMPLES (and the previous crossing has been released), FLAG_THN_PSD pulses for one clock and an integrator slot is armed. Dropping below TH_PSD for a single clock resets the counter to zero, so the OVR_SAMPLES clocks must be consecutive.


Integration path and the pre-trigger delay line

The raw 64-bit word is pushed into a 128-entry circular delay line (one entry = one clock = 4 samples) and read back PRETRG clocks later:

c
  delay1[write_pointer1] = data;
read_pointer1 = write_pointer1 - (int)offset_samples;   // offset_samples = PRETRG
if (read_pointer1 < 0) read_pointer1 += 128;
  

Four parallel integrators (PARALLEL_INTEGRATOR = 4) are available. When an event is armed, the first slot that is neither integrating nor waiting to be committed is taken; its accumulator, sample counter and peak register are cleared and its start timestamp is latched. While a slot is running it adds the four delayed lanes every clock and keeps the maximum of the four lanes:

c
  p_integral[i] += datad_0 + datad_1 + datad_2 + datad_3;   // one clock = 4 samples
if (datad_max > p_maxv[i]) p_maxv[i] = datad_max;
  

After INT_SP clocks the slot stops and is marked to be committed.


Pile-up handling (Pileup property)

The allocation loop walks the slots from 0 upwards; every busy slot it passes before finding a free one has its peak-running flag cleared. What that means depends on the property, which is injected as a #define at the top of the generated .cpp:

Pileup #define emitted Behaviour of the older, still-running events
Stop Integral STOP_INTEGRAL_ON_PILEUP 1 integration stops immediately and the event is committed with a short window
Continue (none) integration runs to the full INT_SP regardless of the new trigger

FLAG_PEAK exports the per-slot peak-running bitmap, so FLAG_INT and FLAG_PEAK differ exactly on the slots that have been hit by a pile-up.


Event output

Committed slots are drained with a fixed priority (slot 0 first, then 1, 2, 3), at most one event per clock. On that clock EVNT_VALID is high for one cycle and EVNT_TS, EVNT_INT, EVNT_PEAK and EVNT_RATIO are valid.

The internal timestamp counts samples: it is incremented by 4 on every clock and cleared to 0 while T0 is high. The value latched when the event is armed is

$$ \mathrm{EVNT_TS} ;=; t_{\text{sample}} ;+; 4\cdot\mathrm{PRETRG} $$

(the core computes timestamp - ((0 - offset_samples) << 2), i.e. it adds four times the pre-trigger; the sign is as written in the source).

EVNT_RATIO depends on the Ratio property:

Ratio #define emitted EVNT_RATIO
External (none) constant 1 — do the division outside the block
Internal INTERNAL_RATIO 1 (EVNT_INT << 8) / EVNT_PEAK, i.e. the ratio in Q8

Monitor outputs

Pin Content
MON_DATA lane 0 of the delayed, offset- and polarity-corrected data
MON_DISCR discriminator output (0 or 6553)
MON_LP low-pass filter output
MON_HP high-pass filter output
MON_INT running integral of slot 0 only
MON_PEAK running peak of slot 0 only
FLAG_INT bits 3:0 = slot busy (16-bit port, upper bits 0)
FLAG_PEAK bits 3:0 = slot peak-running

MON_INT / MON_PEAK are not an event stream — they follow slot 0 and are meaningless while slot 0 is idle.


Generated hardware, clock and build

  • CompileHDL writes HLS/<unique>/isispsd.cpp (with the #defines prepended) and a hls_compile.tcl that runs csynth_design with set_directive_top -name <unique> isispsd, targeting the project FPGA part and a clock period of floor(1000000 / AcquisitionClockFreq) ns — 4 ns on the NI DAQ121, whose CLK_ACQ is 250 MHz. All generated VHDL is merged into HDL/pcores/<unique>.vhd.
  • The IP name is isispsd_<8 letters> where the letters are an MD5 of (component name, ADC BITS, Time Mux, Ratio).
  • The core is free-running: #pragma HLS PIPELINE II=1 and #pragma HLS INTERFACE ap_ctrl_none port=return, so there is no start/done handshake. ap_clk is tied to the project acquisition clock automatically — there is no CLK pin on the symbol — and ap_rst to the project global reset when RESET is left open.

There is no CompileMMC / CompileSDK content: the block exposes no software registers. Everything is configured through pins, so use constant blocks or a register block to drive TH, TH_PSD, OVR_SAMPLES, PRETRG and INT_SP.

Resources & Timing

  • Latency: Declared block latency: 20 clocks with Ratio = External, 55 clocks with Ratio = Internal

  • Throughput: One 64-bit word (4 samples) per clock, II = 1; at most one event per clock on EVNT_VALID

  • 4 concurrent integrators (PARALLEL_INTEGRATOR = 4); a 5th simultaneous event is dropped.
  • 128-entry × 64-bit pre-trigger delay line.
  • Filter coefficients are ap_fixed<18,2>, filter states ap_fixed<24,16>.
  • Synthesised for a clock period of floor(1000000 / AcquisitionClockFreq) ns (4 ns on the NI DAQ121, 250 MHz); LUT/DSP/BRAM figures are whatever Vitis reports for that run and are not fixed by the source.