Xilinx
Block Preview

Introduction

Principle of Operation

The TOF Spectrum block implements a 1D histogram that accumulates time-of-flight measurements in real-time on the FPGA.

Unlike the standard Spectrum block (which histograms amplitude values), the TOF Spectrum measures time intervals between:

  • T0: Reference timing signal (e.g., accelerator pulse, chopper, trigger)
  • IN: Detector event signal

Each time bin corresponds to a specific time window after T0:

$$ bin_{index} = \left\lfloor \frac{t_{event} - t_{T0} - T_{delay}}{T_{bin}} \right\rfloor $$

where:

  • $t_{event}$ = time of detector event (IN rising edge)
  • $t_{T0}$ = time of reference signal (T0 rising edge)
  • $T_{delay}$ = configurable start delay
  • $T_{bin}$ = configurable bin width (in clock cycles)

TOF Spectrum Concept

Pin Description

IN Input 1 bit BIT
Detector input (1-bit). Connect to the discriminator or trigger output of the detector. Rising edges are counted and histogrammed based on their arrival time relative to T0.
T0 Input 1 bit BIT
Time reference (1-bit). Rising edge resets the internal time counter and starts a new measurement cycle. Connect to accelerator pulse, chopper signal, or any periodic reference.
CLK Input 1 bit BIT
Timing clock. All time measurements are relative to this clock. Higher frequency = better time resolution. Default: CLK_ACQ.
Default: Default Board Clock
RESET Input 1 bit BIT
Global reset. Clears the state machine and histogram memory.
Default: Default Board Reset

Properties

Property window

Name EndpointName

Set the name of the endpoint

Logical endpoint name used in the register map. Used by Resource Explorer and SciSDK.

Default: TOF_0

Number of Bins Bins

Number of bins in the spectrum

Number of time bins in the histogram. Options: 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384. More bins = longer measurement window or finer resolution.

Default: 1024

Options: 32 64 128 256 512 1024 2048 4096 8192 16384

Bin Height Bits

Height in bits of each bin

Number of bits per bin (maximum counts per bin). Options: 16, 24, 32 bits.

Bits Max Counts
16 65,535
24 16,777,215
32 4,294,967,295

Default: 24

Options: 16 24 32

Detailed Operation

Data Flow

flowchart LR
  T0(["T0"]) --> TCH["Time Counter<br/>+ Hist"]
  IN(["IN"]) --> TCH
  CLK(["CLK"]) --> TCH
  TCH --> MEM[("Memory<br/>(N bins)")] --> PC("PC")
  TCH --> STATUS["STATUS"] --> RIC(["Running/Idle/Completed"])

Operation Sequence

  1. Software configures bin width and start delay via registers
  2. Software starts acquisition via CONFIG register
  3. On T0 rising edge:
    • Time counter resets to zero
    • After start delay, histogram accumulation begins
  4. On each IN rising edge:
    • Current time bin is incremented
    • Events are accumulated until bin window ends
  5. Time counter advances through all bins
  6. Process repeats on next T0

Timing Diagram

 

State Machine

The block implements a 7-state FSM:

State Code Description
CLEAR_START 000 Begin memory clear
CLEARING 001 Clear all bins to zero
RESET_DONE 111 Ready for acquisition
WAIT_DELAY 010 Wait for start delay after T0
HISTOGRAM 011 Active histogramming
SAVE_RAM 100 Write accumulated count to RAM
SAVE_RAM_2 101 RAM write completion
SCAN_DONE 110 Acquisition complete

Configuration Parameters

Parameter Register Description
Bin Width CONFIG_BINWIDTH Width of each time bin in clock cycles. Minimum 3
Start Delay CONFIG_START_DELAY Delay from T0 before starting histogram
Start/Stop CONFIG[0] 1 = Start, 0 = Stop
Reset CONFIG[1] 1 = Clear histogram (edge triggered)

The Bin Period, Exactly

One time bin lasts exactly CONFIG_BINWIDTH clock cycles, so the bin a detector event falls into is

$$ bin_{index} = \left\lfloor \frac{t_{event} - t_{T0} - T_{delay}}{T_{bin}} \right\rfloor $$

with every bin boundary at an exact multiple of T_bin.

Two details worth knowing:

  • The last two clock cycles of every bin period are the write-pointer advance. An event arriving in those two clocks is not lost - it is carried over and counted in the next bin.
  • The minimum bin width is 3 clock cycles, because that pointer advance plus the reload of the bin counter costs 3 clocks per bin. The RTL saturates a smaller value to 3, and SPECTRUM_<name>_SET_PARAMETERS returns -1 without writing anything, so a program is told rather than silently given a coarser time axis than it asked for.
  • The last bin of a scan collects for T_bin - 2 clocks instead of T_bin: it needs no pointer advance, so the scan ends 2 clock cycles before N_bins * T_bin.

Changed in 2026-10-11 and later bitstreams - READ THIS BEFORE COMPARING OLD AND NEW DATA. Earlier releases made a bin last CONFIG_BINWIDTH + 3 clock cycles, so the whole time axis, and T_window with it, was stretched by 3 / CONFIG_BINWIDTH - +3 % at the bin width of the example below (100), +30 % at a bin width of 10. A histogram recorded with an older bitstream therefore has a time axis 3 % LONGER than the same acquisition recorded now: overlaying the two shifts every peak, and a time of flight computed from the old axis is 3 % too large. To reproduce an old acquisition exactly, program CONFIG_BINWIDTH with the old value minus 3.

Pile-Up: How Close Two Events May Be

No event spacing loses a count. The bin memory accepts one update every 3 clock cycles, and events arriving inside that window are accumulated and written together as soon as it frees up - so two events on consecutive detected edges both count, and so do ten.

What the carry does change is which bin a very late event lands in: a count still pending when the write pointer moves on is written into the bin that is current when the memory frees up. In practice that is the next bin, i.e. the same attribution an event in the last two clocks of a bin period gets anyway. Two counts never merge and none is dropped.

A count still pending when the measurement window ends - a clear request, a new T0, the end of the scan - is discarded, because it has no bin to belong to in the next cycle.

Changed in 2026-10-11 and later bitstreams. Earlier releases lost exactly one count out of every pair of events closer than 3 clocks: the second event read the bin value back before the first event’s write had landed and overwrote it. Measured on a 600-sample test, every affected bin held 1 count instead of 2. A histogram recorded with an older bitstream is therefore under-counted wherever the instantaneous event rate was high, which biases peaks downwards exactly where the statistics matter most.

Clearing the Histogram

CONFIG[1] is edge triggered: one rising edge starts exactly one clear pass, which walks the whole memory in N_bins + 1 clock cycles (33 clocks for a 32-bin histogram) and then returns STATUS to 0 (Stopped) even if the bit is still high. Holding the bit does not keep the block clearing and does not restart the pass; the SDK SPECTRUM_<name>_RESET function already pulses it (write 2, then 0).

While the pass runs STATUS reads 4 (Clearing) and every bin reads zero for the whole pass, including bin 0: a host may read the histogram during a clear and will get zeros, never a leftover value.

Changed in 2026-10-11 and later bitstreams. Before that release the clear was level triggered - holding CONFIG[1] restarted the pass on every clock - and the first write of each pass stored the last accumulated value into bin 0 instead of zero. The final histogram was still correct (a wrap-around write repaired bin 0 at the end of the walk), so the only visible symptom was a non-zero bin 0 read back while STATUS = 4.

Time Resolution

The time resolution depends on the clock frequency and bin width:

$$ \Delta t = \frac{T_{bin}}{f_{CLK}} $$

Clock Bin Width Time Resolution
125 MHz 3 (minimum) 24 ns
125 MHz 10 80 ns
125 MHz 100 800 ns
500 MHz 3 (minimum) 6 ns
500 MHz 10 20 ns

Total Measurement Window

$$ T_{window} = N_{bins} \times T_{bin} / f_{CLK} $$

(exact to within the 2 clock cycles the last bin does not need for its pointer advance)

Bins Bin Width Clock Window
1024 100 125 MHz 819.2 µs
4096 10 125 MHz 327.7 µs
8192 3 500 MHz 49.2 µs

Use Cases

  • Neutron scattering: Time-of-flight from chopper to detector
  • Mass spectrometry: Ion flight time distribution
  • Particle physics: Time-of-arrival histograms
  • LIDAR: Round-trip time distribution
  • Fluorescence lifetime: Decay time histograms

Software Integration

The TOF Spectrum is memory-mapped and can be read via SciSDK.

Available SDK Functions

Function Description
SPECTRUM_<name>_START Start acquisition
SPECTRUM_<name>_STOP Stop acquisition
SPECTRUM_<name>_RESET Clear histogram
SPECTRUM_<name>_SET_PARAMETERS Configure bin width and start delay. Returns -1 and writes nothing if the bin width is below 3
SPECTRUM_<name>_STATUS Get acquisition status
SPECTRUM_<name>_DOWNLOAD Read histogram data

Status Values

Value Meaning
0 Stopped
1 Running
2 Idle (completed)
4 Clearing

Python Example

python
  from scisdk.scisdk import SciSDK

sdk = SciSDK()
sdk.AddNewDevice("usb:10500", "dt5560", "board0", "RegisterFile.json")

# Configure: 100 clock cycles per bin, 10 cycles start delay
sdk.SetParameter("board0:/MMCComponents/TOF_0.bin_width", 100)
sdk.SetParameter("board0:/MMCComponents/TOF_0.start_delay", 10)

# Reset and start
sdk.ExecuteCommand("board0:/MMCComponents/TOF_0.reset", "")
sdk.ExecuteCommand("board0:/MMCComponents/TOF_0.start", "")

# Wait for data...
time.sleep(10)

# Read histogram
res, data = sdk.ReadData("board0:/MMCComponents/TOF_0")

# Plot TOF spectrum
import matplotlib.pyplot as plt
plt.bar(range(len(data)), data)
plt.xlabel("Time Bin")
plt.ylabel("Counts")
plt.title("Time-of-Flight Spectrum")
plt.show()
  

Resources & Timing

  • Latency: 2-3 clock cycles from IN edge to bin update

  • Throughput: Any event rate: one memory update every 3 clocks, each carrying as many counts as arrived in between, so no event is ever lost to pile-up

  • Uses dual-port BRAM for histogram storage
  • One port for time-domain accumulation
  • Second port for software readout
  • Bin saturation prevents overflow wrap-around
  • T0 resets time counter for each measurement cycle
  • One bin lasts exactly CONFIG_BINWIDTH clock cycles, minimum 3