Xilinx
TM
HLS
Block Preview

Introduction

Per lane, over each block of N samples:

$$ \mathrm{MAD}i = \frac{1}{N}\sum{k=0}^{N-1}\bigl|,x_i[k] - \tfrac{1}{N}\textstyle\sum_j x_i[j],\bigr| $$

This is the time multiplexed (TM) twin of the scalar Block Mean Abs Deviation block: TM Factor independent channels packed on one wide bus, each computing the mean absolute deviation about its own block mean, of its own block of N consecutive samples. Nothing is shared between the channels except the frame.

Like sigma it measures spread, but it weights every sample by its distance instead of by its distance SQUARED, so one big outlier moves it far less - per lane. It is about the MEAN, not the median, and needs no sorting.

The TM contract

  • IN is TM Factor lanes of input width bits each, lane 0 in the LOW bits (lane 0 is the oldest sample of the clock) - the same packing as every other TM block in the toolchain. Each lane is an independent channel: its own accumulator, its own sample buffers, its own result.
  • ONE shared EXP pin, ONE IN_DV, one frame: all lanes start and end their blocks on the same accepted clocks. BUSY, INTEGRATING and SAMPLE_COUNT therefore stay scalar - SAMPLE_COUNT counts per-lane samples, which are identical in every lane by construction.
  • THE SAMPLE BUFFERS ARE PING-PONG, PER LANE - this is the one block of the TM family that is NOT constant memory. The BRAM cost is the scalar block’s ping-pong buffer TIMES TM FACTOR: 2 x 2^MaxBlockExponent samples PER LANE. The ping-pong is not an optimisation target: a single buffer with the writes gated during the scan was a VERIFIED dead end on the scalar twin (the scan overruns the block, the block after the dropped one silently loses its first samples), and it applies to every lane here.
  • THE SECOND PASS IS NOT SERIALISED BY LANE. The scan keeps ONE shared address counter, but the per-sample work |N*x - S1| - a shift, a subtract, an abs, NO multiplier - runs for ALL lanes IN PARALLEL, one adder per lane, each lane reading its OWN buffer through its own synchronous-read holding register. (A lane-serialised scan would cost TM Factor x N clocks and break the every-other-block contract.) Only the final requantisation is lane-multiplexed - one lane per clock.
  • ONE OUT_DV per emitted block, after the LAST lane’s requantisation. All output lanes are staged as each lane finishes and committed together on the OUT_DV clock, so every packed output moves on that clock and no other.

N is a runtime input: the block size is $N = 2^{\mathrm{EXP}}$, EXP clamped to Max Block Exponent and latched on the first accepted sample of a block, so a change takes effect on the NEXT block - for every lane at once. Because N is a power of two, every division by N is an exact shift.

When to use this instead of TM Factor scalar blocks

One TM block and TM Factor scalar blocks compute the same numbers, at the same half rate, and - uniquely in this family - the TM block does NOT save memory: the buffers are per lane either way, so the BRAM bill is the same 2 x 2^MaxBlockExponent samples per channel. What it shares is the frame, the control FSM and the scan address generation. The price is the coupling of the frame: all lanes must share the same block length and the same sample cadence. Channels that need different block sizes need scalar blocks.

Pin Description

IN Input (IN_BitsInt + IN_BitsFract) * TMFactor bit TM
Packed TM sample bus: TM Factor lanes of input width bits, lane 0 in the LOW bits. Each lane is an independent channel, accumulated and written into its OWN ping-pong buffer only on the clocks where IN_DV is high.
Default: Must be connected
IN_DV Input 1 bit BIT
Per-sample qualifier for ALL lanes at once, active high, the only qualifier this block has (no CE pin, by family rule - gate this instead). Unconnected defaults to ‘1’. Gaps large enough for the tail restore one-result-per-block emission.
EXP Input 6 bit BIT VECTOR
Block size exponent, shared by every lane: each lane’s block is 2^EXP samples of THAT lane. 6 bits, clamped to Max Block Exponent (capped at 14 on this block), latched on the first accepted sample of a block. Unconnected defaults to 10 (N = 1024).
MEAN_ABS_DEV Output (MAD_BitsInt + MAD_BitsFract) * TMFactor bit TM
Packed per-lane mean absolute deviations about each lane’s own block mean, lane 0 in the LOW bits. On a continuous stream it updates once every TWO blocks (see “Timing”).
OUT_DV Output 1 bit BIT
ONE one-clock pulse per EMITTED block, L clocks after the N-th sample was accepted - on a continuous stream that is every OTHER block. Every packed output updates on this clock and on no other.
CLK 1 bit
Clock.
RESET 1 bit
Synchronous reset: clears every lane (buffers included), the shared frame and the tail.
BUSY 1 bit
Shared by all lanes: high from the first sample of a block until the LAST lane’s result is out (it covers the whole two-pass tail); its last high clock is the OUT_DV pulse. Present only when Enable BUSY = YES.
INTEGRATING 1 bit
Shared by all lanes: high only while the block is accumulating. The clock it falls on is the one where SAMPLE_COUNT always reads N. Present only when Enable INTEGRATING = YES.
SAMPLE_COUNT 1 bit
Shared by all lanes (they advance in lockstep): samples accumulated so far in the current block, 1 after the first, N after the N-th. NOT cleared at the block end; on a continuous stream it has already restarted by OUT_DV, so latch it when INTEGRATING falls. 32 bits. Present only when Enable SAMPLE_COUNT = YES.

Properties

Property window

IN Integer Bits IN_BitsInt

Number of INTEGER bits of the input sample (per lane) (the sign, when present, uses one of them).

Integer bits of ONE LANE of the input (the sign, when present, uses one of them). 1..64. Default 16.

Default: 16

Options: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

IN Fractional Bits IN_BitsFract

Number of FRACTIONAL bits of the input sample (per lane), i.e. the bits to the right of the binary point. Total width = integer + fractional bits, and must not exceed 64.

Fractional bits of one lane of the input. 0..64, total lane width 2..64 bits. Default 0.

Default: 0

Options: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

IN Sign IN_Sign

Select whether the input sample (per lane) is signed (two’s complement) or unsigned.

SIGNED (two’s complement) or UNSIGNED lanes. Default SIGNED. Applies to every lane.

Default: SIGNED

Options: UNSIGNED SIGNED

TM Factor TMFactor

Number of INDEPENDENT time-multiplexed channels packed on the IN bus and on every result bus. Lane 0 occupies the LOW bits (lane 0 = the oldest sample of the clock), the same packing as every other TM block. All lanes share one EXP / IN_DV / frame; each lane gets its own accumulators, but the serial post-processing is ONE engine serving the lanes one after the other, so the tail latency (and the minimum usable EXP) grows with this factor.

Number of independent channels packed on the buses, 2..16. Default 4. Multiplies the IN width, the packed output width, THE NUMBER OF PING-PONG SAMPLE BUFFERS (the BRAM bill is per lane) and the requantisation clocks of the tail. It also raises the minimum usable EXP (TM 4 needs EXP >= 3, TM 16 needs EXP >= 5).

Default: 4

Options: 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Max Block Exponent MaxBlockExponent

Largest block-size exponent the accumulators are sized for: the block can be up to 2^MaxBlockExponent samples long. The EXP input is clamped to this value at run time. Raising it widens the internal accumulators, and ON THE BLOCKS WHOSE SERIAL ENGINES ARE SIZED FROM THOSE ACCUMULATORS (Coefficient of Variation, SNR, Skewness, Kurtosis, Correlation, Autocorrelation, Linear Regression) it also LENGTHENS THE SERIAL TAIL – even when the runtime EXP is small. Keep it at the largest block you actually use. The default of 10 covers blocks of up to 1024 samples. HERE IT ALSO SIZES A MEMORY: the block keeps 2 x 2^MaxBlockExponent samples PER LANE, so it is capped at 14 instead of the 31 the rest of the family allows.

Largest block-size exponent, and ON THIS BLOCK IT ALSO SIZES A MEMORY: each lane keeps 2 x 2^MaxBlockExponent samples of ping-pong buffer, so the total BRAM cost is the scalar block’s buffer times TM Factor. That is why it is capped at 14 here instead of the 31 the rest of the family allows. The EXP input is clamped to it at run time. 1..14, default 10.

Default: 10

Options: 1 2 3 4 5 6 7 8 9 10 11 12 13 14

MAD Integer Bits MAD_BitsInt

Number of INTEGER bits of the MEAN_ABS_DEV output (per lane) (the sign, when present, uses one of them).

Integer bits of ONE LANE of the MEAN_ABS_DEV output. 1..64, default 16.

Default: 16

Options: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

MAD Fractional Bits MAD_BitsFract

Number of FRACTIONAL bits of the MEAN_ABS_DEV output (per lane), i.e. the bits to the right of the binary point. Total width = integer + fractional bits, and must not exceed 64.

Fractional bits of one lane. 0..64, default 0. A mean deviation can be well below one input LSB on a quiet lane - fractional bits are worth having.

Default: 0

Options: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

MAD Sign MAD_Sign

Select whether the MEAN_ABS_DEV output (per lane) is signed (two’s complement) or unsigned.

SIGNED or UNSIGNED lanes. Default UNSIGNED (a mean absolute deviation is non negative by construction).

Default: UNSIGNED

Options: UNSIGNED SIGNED

Enable BUSY EnableBusy

YES: the BUSY (high from the first sample of a block until its result is out – it COVERS THE SERIAL TAIL, and its last high clock IS the OUT_DV pulse) pin is present. NO: the pin AND all of its logic are removed BEFORE synthesis, so nothing is paid for it.

YES: the shared BUSY pin exists. Default NO.

Default: NO

Options: NO YES

Enable INTEGRATING EnableIntegrating

YES: the INTEGRATING (high only while the block is ACCUMULATING; it drops as soon as the N-th sample has been taken and the tail starts, so BUSY-and-not-INTEGRATING means ‘computing’) pin is present. NO: the pin AND all of its logic are removed BEFORE synthesis, so nothing is paid for it.

YES: the shared INTEGRATING pin exists. Default NO.

Default: NO

Options: NO YES

Enable SAMPLE_COUNT EnableSampleCount

YES: the SAMPLE_COUNT (32 bit, how many samples have been accumulated so far in the current block: 1 after the first, N after the N-th. It is NOT cleared at the block end – it holds N until the NEXT block’s first accepted sample takes it back to 1. On a CONTINUOUS stream that happens DURING the serial tail, so at OUT_DV it reads how far into the next block the input has already got, NOT N. To capture the length of the block being presented, latch SAMPLE_COUNT on the clock INTEGRATING falls – that one always reads N) pin is present. NO: the pin AND all of its logic are removed BEFORE synthesis, so nothing is paid for it.

YES: the shared 32 bit SAMPLE_COUNT pin exists. Default NO.

Default: NO

Options: NO YES

Rounding Rounding

ROUND: round to nearest when a result has to be requantised into a coarser output format. TRUNCATE: drop the bits (cheaper, adds a negative bias).

ROUND: round to nearest at each lane’s single final requantisation (nothing else in the lane ever rounds). TRUNCATE: drop the bits. Default ROUND.

Default: ROUND

Options: TRUNCATE ROUND

Saturation EnableSaturation

YES: clip to the largest representable value of each output format (symmetric for signed formats). NO: wrap around.

YES: clip each lane to its output format (symmetric for signed). NO: wrap. Default YES.

Default: YES

Options: NO YES

Accuracy, per lane

Each lane is BIT EXACT, like the scalar twin - because the mean is NEVER rounded: each lane computes mad = sum|Nx - S1| / N^2, where Nx is a shift, so no per-sample rounding error exists anywhere. The only requantisation is the single final one into the per-lane MAD format. The harness demands tolerance 0 per lane.

Timing: HALF RATE - one result every two blocks

This block does NOT follow the family’s usual lane-multiplexed tail rule. The mean of a lane is not known until that lane’s last sample has been seen, so pass two re-reads the whole block: the tail is LONGER THAN THE BLOCK ITSELF,

$$ L = 2^{\mathrm{EXP}} + \mathrm{TM_FACTOR} + 2 $$

(BMDT_TAIL in the core: N scan clocks shared by all lanes in parallel, plus one requantise clock per lane, plus the registered OUT_DV; at TM Factor = 1 it would reduce to the scalar’s 2^EXP + 3). At the defaults (TM 4, EXP 10) that is 1030 clocks against a 1024-clock block.

The family’s drop rule - a block that completes while the tail is still running is silently dropped, no OUT_DV, no error pin - therefore applies EVERY OTHER BLOCK on a continuous stream: result, no result, result, no result, in every lane at once. OUT_DV tells you which blocks produced an answer. With input gaps large enough for the tail to finish before the next block completes, it emits every block - same as the scalar. The minimum exponent is therefore NOT “the exponent at which the tail fits” (there is none); it is the ALTERNATE-BLOCK-EMISSION condition $2\cdot 2^{\mathrm{EXP}} \ge L$ - the tail must fit inside TWO block periods or nothing is ever emitted. The TM term makes it larger than the scalar twin’s: TM 4 needs EXP >= 3, TM 16 needs EXP >= 5. The compiler prints the tail and the minimum EXP in the compilation log, and the property window refuses a configuration whose minimum exceeds Max Block Exponent.

Knowing where the block is: BUSY, INTEGRATING and SAMPLE_COUNT

Identical to the scalar family, and SHARED by all lanes: INTEGRATING is high exactly while the block is accumulating (it dips one clock per block boundary on a continuous stream), BUSY also covers the tail and its last high clock IS the OUT_DV pulse, SAMPLE_COUNT reads 1 after the first accepted sample and N after the N-th.

SAMPLE_COUNT is NOT cleared at the block end - it holds N until the NEXT block’s first accepted sample takes it back to 1. On a CONTINUOUS stream that happens DURING the tail (which here is longer than a whole block), so at OUT_DV it reads how far into the next block the input has got, NOT N. The clock that always reads N is the one INTEGRATING falls on - latch it there.

Verification

The core is regression tested by a host-side csim harness (tb/block-ops-tm/run_tb_tm.ps1) with per-lane goldens computed by gen_golden_tm.py in exact rational arithmetic ON EACH LANE’S STREAM ALONE (lanes deliberately carry different signals - the generator refuses identical lanes), with the every-other-block drop schedule predicted by the same frame model that checks the status pins. Plus the strongest available lane-independence check: after every run, the SCALAR twin is replayed on each lane’s stream by itself and lane k of every TM result must match it BIT FOR BIT, with the emitted-block ordinals aligning the two streams across the drops. The status waveform is checked clock by clock, every packed output is checked to move only on OUT_DV, and the TM-specific mutant classes (shared accumulator or buffer, lane swaps, wrong-lane reads, early commit, ping-pong collapse) are killed. What no host harness can prove - that Vitis accepts and schedules the core at II=1, with the per-lane RAM_S2P ping-pong buffers - is stated in AGENT/block_ops.log, not silently implied.