HLS
Block Preview

Introduction

Principle of Operation

Trapezoidal Shaper HLS is the “everything after the trigger” half of a spectroscopy chain. The C source (PHA_chain.cpp, top function PHAchain) wires three stages in series, all pipelined at one sample per clock:

  DATA_IN ─►[ HLSDPP_trapezio ]─┬─► TRAP MON
            k, m, M, G        │
                              ├─►[ HLSDPP_baseline_restorer ]─► BL MON
                              │        BL_LEN, BL_INIB        │
                              │                               ▼
                              └────────►[ HLSDPP_energysampler ]─► ENERGY
TRIGGER ──────────(+1 clk)──────────────►  SAMPLE_POS, PUR      ENERGY DV
TIMETAG ────────────────────────────────►  PUR_MODE, PILEUP_INIB  TIMESTAMP
  

Where it sits among the trapezoid blocks

Trapezoidal Shaper FP Trapezoidal Shaper HLS (this page) TRP MCA MCA / MCA HLS
internal name trapezoidal PHAchain trp_trapezoidal mcahp / mcahp_test
trigger filter none none — external TRIGGER built in (TRIG_K/TRIG_M/THRS) built in
pole-zero deconvolution yes (M, 32 bit) yes (DECONV_M, 24 bit) deliberately absent yes
baseline supplied from outside (BASELINE pin) computed internally + hold computed internally + leakage model computed internally + hold
energy sampler no — raw shaper stream out yes (SAMPLE_POS) yes yes
pile-up rejection no yes (3 modes) high-rate mode only MCA Short Signal
time stamping no yes yes only MCA HLS
output SHAPER_OUT 16 bit + DV ENERGY 32 bit + ENERGY DV ENERGY 32 bit ENERGY 32 bit
HLS tool-chain needed no yes yes no / yes

So “chain” is literal: compared to Trapezoidal Shaper FP, which is a bare shaper that streams a 16-bit shaped waveform and expects you to build the rest, this block adds the baseline restorer with trigger-driven hold, the energy sampler, a three-mode pile-up rejector and a 64-bit time-stamp latch, and widens the result to 32 bits. Compared to TRP MCA it keeps the exponential deconvolution (TRP MCA drops it because a transistor-reset preamplifier has no decay constant) and drops the leakage estimator, the reset detector and the built-in trigger filter.

Use it when your trigger already exists somewhere else in the design — a Fast Shaper Trigger, a CFD, a coincidence unit or an external input — and you only need the energy branch.

Pin Description

DATA_IN Input 16 bit BIT VECTOR
ADC samples, 16 bit, one per clock. Fed to the shaper unmodified — see the note about POLARITY / OFFSET.
Default: Must be connected
TRIGGER_IN Input 1 bit BIT
Trigger, one clock high per event (TRIGGER on the canvas). This block has no internal trigger filter: without a pulse here nothing is ever emitted. It starts the energy-sampling delay, freezes the baseline for BL_INIB samples and drives the pile-up state machine — which watches it as a level, so it must be a single-clock pulse.
Default: Must be connected
POLARITY Input 1 bit BIT
Present on the symbol and wired to the core, but not used by any stage of PHA_chain.cpp. Invert negative-going pulses upstream. Unconnected it is driven to '1'.
Default: 1
BIN_NUM Input 8 bit BIT VECTOR
Effective number of ADC bits (BIT_NUM on the canvas). Present on the symbol and wired to the core, but not used by any stage of PHA_chain.cpp.
OFFSET Input 16 bit BIT VECTOR
Digital offset. Present on the symbol and wired to the core, but not used by any stage of PHA_chain.cpp. Subtract the pedestal upstream.
TRAP_K Input 16 bit BIT VECTOR
First differentiator delay, in samples. Together with TRAP_M it sets a trapezoid of rise $\min(K,M)$ and flat top $|K-M|$. Must be smaller than Max shaper samples. Unconnected it is driven to x"0100" (256).
Default: 256
TRAP_M Input 16 bit BIT VECTOR
Second differentiator delay, in samples — not the flat top. Must be smaller than Max shaper samples. Unconnected it is driven to x"010A" (266), giving a 10-sample flat top with the default TRAP_K.
Default: 266
DECONV_M Input 24 bit BIT VECTOR
Pole-zero coefficient, 24 bit, scaled by 256: DECONV_M = 256 / (exp(Ts/tau) − 1). 0 disables the deconvolution. Unconnected it is driven to x"007C80".
Default: 31872
TRAP_GAIN Input 24 bit BIT VECTOR
Output gain, Q8.16 (0x010000 = ×1.0), applied after the fixed $2^{24}$ descaling. Unconnected it is driven to x"010000".
Default: 65536
BL_LEN Input 4 bit BIT VECTOR
Baseline averaging exponent: window = $2^{\rm BL_LEN}$ samples, which must not exceed Max baseline samples. Only sampled while RUN(1) CFG(0) is low — changing it on the fly corrupts the baseline. Unconnected it is driven to x"8" (256 samples).
Default: 8
BL_INIB Input 16 bit BIT VECTOR
Baseline hold, in samples after each trigger. Must exceed the full trapezoid duration. Unconnected it is driven to x"1000" (4096).
Default: 4096
SAMPLE_POS Input 16 bit BIT VECTOR
Energy sampling position, in samples after the trigger; the strobe actually lands at SAMPLE_POS + 6. Aim for the middle of the flat top. Must be smaller than Max shaper samples − 5. Unconnected it is driven to x"0108" (264).
Default: 264
PUR_MODE Input 4 bit BIT VECTOR
Pile-up rejection mode: 0 = none, 1 = dead time after each accepted event, ≥ 2 = full rejection (both members of a pile-up pair are discarded). See the table above.
PILEUP_INIB Input 16 bit BIT VECTOR
Pile-up guard length, in samples, added to SAMPLE_POS to form the window used by PUR_MODE 1 and ≥ 2. Ignored when PUR_MODE = 0. Set it to at least the fall time of the trapezoid.
TIMETAG Input 64 bit BIT VECTOR
Free-running time counter, 64 bit, sampled onto TIMESTAMP at the instant the energy is accepted. Leave unconnected if you do not need time stamps.
RUN_CFG Input 1 bit BIT
Run (1) / configure (0). Low clears the pointers and accumulators and latches BL_LEN. On the 0→1 edge every stage reloads its pre-roll counter, so expect several thousand samples of dead time before the first energy.
CLK Input 1 bit BIT
Clock, defaults to the project acquisition clock. One DATA_IN sample is consumed per rising edge; the HLS core is closed at the project acquisition-clock period.
Default: Default Board Clock
RESET Input 1 bit BIT
Reset, defaults to the project global reset.
Default: Default Board Reset
ENERGY Output 32 bit BIT VECTOR
Energy, 32 bit signed. Continuously equal to (shaper − baseline) — the same value as TRAP-BL MON — so it is only meaningful on the clock where ENERGY DV is high. Latch it there.
ENERGY_STROBE Output 1 bit BIT
Energy data valid (ENERGY DV on the canvas). One clock high, SAMPLE_POS + 6 samples after the trigger, for every event that survives PUR_MODE.
BASELINE_HOLD Output 1 bit BIT
High while the baseline average is frozen (during the BL_INIB window after a trigger). Permanently high means BL_INIB is too long for the current rate.
TRAP_MON Output 32 bit BIT VECTOR
Monitor: raw shaper output before baseline subtraction. Scope it to tune DECONV_M — a correct pole-zero gives a flat top and no undershoot after the trapezoid.
TRAP_BL_MON Output 32 bit BIT VECTOR
Monitor: (shaper − baseline). Identical, clock by clock, to ENERGY. Scope it against TRIGGER to place SAMPLE_POS.
BL_MON Output 32 bit BIT VECTOR
Monitor: the current baseline value.
TIMESTAMP Output 64 bit BIT VECTOR
Time stamp, 64 bit: the value of TIMETAG at the moment the energy was accepted. Driven to 0 on every other clock, so it must be captured on ENERGY DV.

Properties

Property window

Max shaper samples PRE_LEN

This parameters configure the number of samples used by the trapezoidal. It impact of the RAM memory usage in the FPGA

Max shaper samples — depth of the two 18-bit shaper delay lines and of the energy sampler’s trigger-flag buffer. It is the ceiling for TRAP_K, TRAP_M and (minus 5) SAMPLE_POS, and it sets the shaper start-up dead time to 2 × this value.

Allowed: 128, 256, 512, 1024, 2048, 4096, 8192, 16384.

Default: 2048

Default: 2048

Options: 128 256 512 1024 2048 4096 8192 16384

Max baseline samples BASELINE_PRE_LEN

This parameters configure the number of samples used by the baseline moving average. It impact of the RAM memory usage in the FPGA

Max baseline samples — depth of the 32-bit baseline ring buffer. It is the ceiling for $2^{\rm BL_LEN}$ and sets the baseline start-up dead time to 2 × this value.

Allowed: 128, 256, 512, 1024, 2048, 4096, 8192, 16384.

Default: 2048

Default: 2048

Options: 128 256 512 1024 2048 4096 8192 16384

Accumulator Size LARGE_ACC_BITS

Size in bit of the signal accumulator. It allows to create a filter that a less logic resources. If the exponentials signal are fast and trapezoidal is short the accumulator can be configured smaller

Accumulator Size — width, in bits, of the second (deconvolution) accumulator. This accumulator wraps, it does not saturate: too few bits with a long trapezoid or a large DECONV_M corrupts large-energy events silently. Reduce it below 64 only for short trapezoids, and verify the peak positions afterwards.

Note that this value is not part of the generated HLS IP name — see the collision warning above.

Allowed: 32, 48, 64, 96.

Default: 64

Default: 64

Options: 32 48 64 96

⚙️ Detailed Operation

The shaper, exactly as the C computes it

HLSDPP_trapezio is the Jordanov–Knoll recursive trapezoid with the pole-zero term folded into the second accumulator. Reading the source line by line, with $k$ = TRAP_K, $m$ = TRAP_M, $M$ = DECONV_M, $G$ = TRAP_GAIN:

$$ s_1[n] = x[n] - x[n-k] $$ $$ s_2[n] = s_1[n] - s_1[n-m] $$ $$ \mathrm{acc}_1[n] = \mathrm{acc}_1[n-1] + s_2[n] $$ $$ s_3[n] = 256,\mathrm{acc}_1[n] + M,s_2[n] $$ $$ \mathrm{acc}_2[n] = \mathrm{acc}_2[n-1] + s_3[n] $$ $$ \mathrm{TRAP}[n] = \frac{\bigl(\mathrm{acc}_2[n] \gg 24\bigr)\cdot G}{2^{16}} $$

Three things follow directly from those lines and are worth knowing before you pick coefficients:

  • The two delays are independent. The rise time of the trapezoid is $\min(k,m)$ samples and the flat top is $|k-m|$ samples. TRAP_M is not the flat top; it is the second differentiator delay. The default pair TRAP_K = 0x0100, TRAP_M = 0x010A is therefore a 256-sample rise with a 10-sample flat top.
  • DECONV_M carries a factor 256, because the plain accumulator is shifted left by 8 before the deconvolution term is added. That is exactly why the pole-zero coefficient is $$ M = \frac{256}{e^{T_s/\tau}-1} $$ with $T_s$ the sample period and $\tau$ the preamplifier decay constant. DECONV_M = 0 disables pole-zero cancellation and leaves you with a plain trapezoid on an exponential tail.
  • The fixed scaling is $2^{24}$ and the gain is Q8.16. The output is divided by $2^{24}$ and then multiplied by TRAP_GAIN/$2^{16}$, so TRAP_GAIN = 0x010000 is a gain of exactly 1.0 and the useful dynamic range is set by choosing TRAP_GAIN so that the largest pulse lands near the top of the 32-bit energy word (or near the top of your spectrum’s bin count).

The accumulator width is a real design parameter

acc1 is a fixed 64-bit accumulator, but acc2 — the one that carries the $2^{24}$-scaled sum — is exactly Accumulator Size bits wide (LARGE_ACC_BITS). It is a plain wrapping integer: nothing saturates. The scaled sum grows with the input amplitude, with the trapezoid length and with the deconvolution coefficient, so a 32-bit accumulator with a long trapezoid and a large DECONV_M will silently wrap and produce a spectrum full of nonsense at high energy. 64 bits (the default) is the safe choice; reduce it only when the trapezoid is short and you have verified the peak position is unchanged.

Baseline restorer

HLSDPP_baseline_restorer keeps a running sum over a window of $2^{\rm BL_LEN}$ samples of the shaper output and reports BL_ACCUMULATOR >> BL_LEN. On every TRIGGER it loads BL_INIB into a dead counter, raises BL HOLD and stops feeding the window until the counter expires, so the pulse itself never enters the average.

BL_INIB must be longer than the entire trapezoid (rise + flat top + fall) or the pulse tail is averaged into the baseline and the peak position drifts with count rate. If BL HOLD is high all the time, BL_INIB is too long for the current rate and the baseline has stopped updating altogether.

Energy sampler, and the two constants hidden in it

The trigger is delayed by exactly one clock inside the shaper stage, then used to address a PRE_LEN-deep flag buffer at an offset of SAMPLE_POS + 5. The measured, deterministic relation is therefore

$$ t(\mathrm{ENERGY\ DV}) = t(\mathrm{TRIGGER}) + \mathrm{SAMPLE_POS} + 6 \ \text{samples} $$

(plus the fixed pipeline latency of the core). The +5 is a hard-coded constant in the C, not a parameter: when you calibrate SAMPLE_POS against a scope trace of TRAP-BL MON, remember you are placing the sample six samples later than the number you write.

Pile-up rejection (PUR_MODE)

PUR_MODE behaviour
0 No rejection and no dead time. Every trigger produces one ENERGY DV, PILEUP_INIB is ignored.
1 Dead time. An accepted event blocks the next SAMPLE_POS + PILEUP_INIB samples; triggers inside that window are discarded silently.
≥ 2 Full pile-up rejection state machine. On a trigger the block waits PILEUP_INIB + SAMPLE_POS samples; if no further trigger arrives, the energy is emitted. If another trigger arrives during the wait, the event is discarded and the machine stays in a guard state, re-arming the counter on every new trigger until PILEUP_INIB + SAMPLE_POS samples pass with no trigger at all.

Mode ≥ 2 is the one you want for spectroscopy at rate: it throws away both members of a pile-up pair rather than distorting the spectrum with them. Note that the state machine watches the raw TRIGGER input (not the delayed copy), so TRIGGER must be a clean one-clock pulse — a trigger held high for several clocks re-arms the guard on every clock and nothing is ever emitted.

Start-up dead time

Each of the three stages holds a pre-roll counter that is loaded with twice its buffer length whenever RUN(1) CFG(0) is low, and decremented one per sample once running. Until it reaches zero the delay lines are written with zeros and no energy is produced:

stage pre-roll after RUN_CFG rises
shaper 2 × Max shaper samples
energy sampler 2 × Max shaper samples
baseline restorer 2 × Max baseline samples (only counted while BL HOLD is low)

With the default 2048/2048 that is 4096 samples of dead time after every RUN_CFG 0→1 edge — 33 µs at 125 MHz. Do not arm your acquisition in the same breath as RUN_CFG.

Parameter ceilings

All four delay lines are addressed with a wrapping pointer and no range check: an offset larger than the buffer aliases onto the wrong sample instead of raising an error. From the source:

parameter ceiling
TRAP_K, TRAP_M < Max shaper samples (PRE_LEN)
SAMPLE_POS < Max shaper samples − 5
$2^{\rm BL_LEN}$ ≤ Max baseline samples (BASELINE_PRE_LEN)

Build-time behaviour

ComponentInfo.UseHLS is set, so the block writes <project>/HLS/<ip>/PHAchain.cpp (the embedded C prefixed with #define PRE_LEN, #define BASELINE_PRE_LEN, #define LARGE_ACC_BITS) plus an hls_compile.tcl, and Vitis HLS synthesises the IP during the build. The clock period handed to HLS is derived from the project acquisition clock. If no HLS tool-chain is found and the build is not a remote/cloud build, CompileHDL fails immediately.

Typical wiring

  ADC ─┬─► Fast Shaper Trigger ─► TRIGGER
     └────────────────────────► DATA_IN
Timebase counter ─────────────► TIMETAG        ENERGY ───┬─► List / Spectrum
Reg ─► TRAP_K TRAP_M DECONV_M TRAP_GAIN        ENERGY DV ┘
Reg ─► BL_LEN BL_INIB SAMPLE_POS               TIMESTAMP ─► List endpoint
Reg ─► PUR_MODE PILEUP_INIB                    TRAP MON / BL MON ─► Oscilloscope
Reg ─► RUN(1) CFG(0)
  

This block declares no memory-mapped registers (CompileMMC / CompileSDK are empty): drive the parameter pins from Register blocks so the SciSDK can write them, and read the results through whatever endpoint you connect ENERGY / TIMESTAMP to.

Resources & Timing

  • Latency: The symbol declares 13 clocks. The C constrains the shaper stage with #pragma HLS latency min=15 and the top function with PIPELINE II=1; the exact RTL pipeline depth is decided by Vitis HLS for your part and clock at build time and is not fixed in the sources. The trigger-to-strobe distance is exact and tool independent: SAMPLE_POS + 6 samples.

  • Throughput: 1 sample per clock (#pragma HLS PIPELINE II=1 on PHAchain)

  • Memory cost scales with the properties: two 18-bit × Max shaper samples RAMs plus one 1-bit × Max shaper samples buffer in the shaper/sampler, and one 32-bit × Max baseline samples RAM in the baseline restorer.
  • The core uses ap_ctrl_none, so there is no start/done handshake — it free-runs on every clock.
  • Ports carry a _V suffix for Vivado versions below 2019.2 and no suffix from 2019.2 on; the block handles this automatically.
  • Start-up dead time after RUN(1) CFG(0) goes high is 2 × Max shaper samples (and 2 × Max baseline samples for the baseline), i.e. 4096 samples with the defaults.