DSP - BLOCK MEAN ABS DEVIATION TM
TM (time multiplexed) twin of Block Mean Abs Deviation: TM Factor (2..16) independent channels packed on one wide bus, each computing mean(|x - mean(x)|) - the average distance of a sample from that lane’s OWN block mean, the outlier-tolerant spread - over its own block of N = 2^EXP samples (EXP on a runtime pin, shared by every lane). READ THIS FIRST: like its scalar twin, ON A CONTINUOUS STREAM THIS BLOCK EMITS ONE RESULT EVERY TWO BLOCKS - the mean is not known until a lane’s last sample, so every lane’s samples are buffered and read a SECOND time, and the tail is longer than the block itself. THE BUFFERS ARE PING-PONG, PER LANE: the BRAM cost is the scalar block’s ping-pong buffer TIMES TM FACTOR - 2 x 2^MaxBlockExponent samples PER LANE - which is why Max Block Exponent is capped at 14 here instead of 31. One shared frame - one EXP, one IN_DV, one OUT_DV per block pair. Lane 0 sits in the LOW bits of every packed bus. Same per-lane numbers as the scalar twin, bit for bit. Optional shared BUSY / INTEGRATING / SAMPLE_COUNT status outputs.
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
INis 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
EXPpin, ONEIN_DV, one frame: all lanes start and end their blocks on the same accepted clocks.BUSY,INTEGRATINGandSAMPLE_COUNTtherefore stay scalar -SAMPLE_COUNTcounts 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_DVper emitted block, after the LAST lane’s requantisation. All output lanes are staged as each lane finishes and committed together on theOUT_DVclock, 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_DV
is high.
Properties
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
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
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
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
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
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
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
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
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
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
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
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
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_COUNTis 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 atOUT_DVit reads how far into the next block the input has got, NOT N. The clock that always reads N is the oneINTEGRATINGfalls 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.