Xilinx
HLS
Block Preview

Introduction

The Block Mean Abs Deviation block chops the input stream into consecutive blocks of N samples and publishes the mean absolute deviation about that block’s own mean:

$$ \mathrm{mad} = \frac{1}{N}\sum_{i=0}^{N-1} \left|, x_i - \frac{1}{N}\sum_{j=0}^{N-1} x_j ,\right| $$

READ THIS ONE FIRST

THIS BLOCK EMITS ONE RESULT EVERY TWO BLOCKS.

It is the only block in the family that cannot be done in constant memory. The mean is not known until the last sample of the block has been seen, so the samples have to be kept and read a SECOND time. Pass two takes N clocks, so the tail is longer than the block itself:

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

and the family’s normal rule - a block that completes while the tail is still running is DROPPED - then applies every other block. For $\mathrm{EXP} \ge 2$ the pattern is exactly alternate: result, no result, result, no result.

Nothing else about the block changes. The accumulators stay correct, the dropped block corrupts nothing, BUSY / INTEGRATING / SAMPLE_COUNT keep their published meaning, and OUT_DV tells you exactly which blocks produced an answer. If you need a result every block, see Alternatives below.

N is a runtime input, not a property. You drive the exponent on the EXP pin and the block size is $N = 2^{\mathrm{EXP}}$:

EXP N EXP N
4 16 10 1024
6 64 12 4096
8 256 14 16384

The table stops at 14 on purpose: here the exponent sizes a memory, not just an accumulator - see The sample buffer.

What it is FOR

The mean absolute deviation is a robust measure of spread. It answers the same question as the standard deviation - how wide is this distribution - but it weights each sample by its distance from the mean rather than by the square of that distance. The practical difference is entirely about outliers:

block of 1024 samples sigma mad
uniform noise, no outlier s ~0.87 s
the same, plus ONE sample 100 sigma out ~3.2 s ~0.97 s

One rare spike nearly triples the standard deviation and barely moves the mean absolute deviation. So use this block when the question is “how wide is the bulk of this distribution” and a rare spike must not be allowed to answer it: baseline width in the presence of real pulses, channel noise on a detector that occasionally fires, a data-quality metric that must not alarm on a single glitch.

For a Gaussian distribution the two are related by a fixed factor,

$$ \mathrm{mad} = \sigma\sqrt{\tfrac{2}{\pi}} \approx 0.7979,\sigma $$

so on clean Gaussian noise you can convert one into the other; the point of using this block is precisely the cases where the noise is not clean and the two therefore disagree.

It is about the MEAN, not the MEDIAN

This block needs NO SORTING. It is mean(|x - mean(x)|), not the median-based median absolute deviation that shares the acronym. The median-based variants - median absolute deviation, interquartile range, arbitrary quantiles - require the block to be ordered, and they are deliberately not in this family. They live in Histogram Analyzer, which gets them out of a histogram for free.

Do not confuse it with Block Mean Abs

Block Mean Abs computes $\mathrm{mean}(|x - \mathrm{LEVEL}|)$ against a level you supply on a pin, in one pass and with no memory. This block computes the deviation about the mean of the same block, which is not known until the block is over. That difference is the entire reason for the two-pass structure and the half-rate output. If a one-block-old mean is good enough for you, wire Block Mean’s output into Block Mean Abs’s LEVEL pin and pay nothing - for a stationary signal that is genuinely the cheaper answer.

When to use this instead of Block Statistics

The all-in-one Block Statistics block is not deprecated, but note first: it has no mean absolute deviation about the block mean. It has MEAN_ABS, which is the level-referred one-pass statistic described just above, and it has the variance and the standard deviation - but not this. So the usual family rule applies with one extra clause:

  • you want several statistics of the SAME block - the mean and the RMS and the min/max of the same N samples - use Block Statistics. They share one accumulator and one serial tail, so the second and third statistic are nearly free.
  • you want exactly one number - use a per-operator block. Then you synthesise only that number: the pin list, the logic and the tail are all that it needs, and nothing else reaches the synthesiser.
  • you want this number - use this block, because there is nowhere else to get it. Pair it with a Block Statistics block on the same stream if you also want the moments.

Two Block Statistics blocks side by side would duplicate the accumulators; two per-operator blocks side by side duplicate them too. One Block Statistics block never does.

Pin Description

IN Input IN_BitsInt + IN_BitsFract bit BIT VECTOR
Input samples, fixed point in the IN Q format. Accumulated and written into the ping-pong sample buffer only on the clocks where IN_DV is high. Every sample of every block is stored, because pass two has to read them all back.
Default: Must be connected
IN_DV Input 1 bit BIT

Per-sample qualifier, active high, and the ONLY qualifier this block has. A sample is accumulated, stored, and counts towards N, exactly on the clocks where this is high. The tail keeps running regardless - pass two is driven by the clock, not by the input - so a gapped stream gives the tail more room and can make the drop pattern less frequent than alternate. Unconnected defaults to '1'. (There is deliberately no CE pin

  • to stall the block, gate this.)
EXP Input 6 bit BIT VECTOR
Block size exponent, runtime programmable: the block is $N = 2^{\text{EXP}}$ samples long, and the result divides by $N^2$. 6 bits unsigned, accepted range 0 .. Max Block Exponent; larger values are clamped to Max Block Exponent, which is at most 14 on this block. Sampled on the first accepted sample of a block and held for that whole block, so a change takes effect on the NEXT block. It sets the latency directly: $L = 2^{\text{EXP}} + 3$, so each step doubles the tail and the interval between results. Unconnected defaults to 10 (N = 1024).
MEAN_ABS_DEV Output MAD_BitsInt + MAD_BitsFract bit BIT VECTOR
$\mathrm{mean}(|x - \mathrm{mean}(x)|)$ of the block, in the MAD Q format (MAD_BitsInt / MAD_BitsFract / MAD_Sign in the property grid). Bit exact - the mean is never rounded, so the only rounding is the final requantisation. Updated on the OUT_DV clock and on no other, which means it holds the last emitted result across the dropped block. Always present: this block has no output enable.
OUT_DV Output 1 bit BIT
One-clock pulse marking a valid result. It fires when pass two completes, $L = 2^{\text{EXP}} + 3$ clocks after the clock on which the N-th sample of the block was accepted. It does NOT fire for every block: because $L > 2^{\text{EXP}}$ always, a block that completes while the scan is still running is dropped, which for EXP >= 2 means exactly every other block. This pin, not the block boundary, is the definition of “a result happened” - count these pulses. MEAN_ABS_DEV is updated on this clock and on no other, and BUSY is still high here and falls on the next clock.
CLK 1 bit
Clock.
RESET 1 bit
Synchronous reset: clears the accumulator, the block counter, the sample count, the scan state, the buffer select and the whole sample buffer.
BUSY 1 bit
High from the start of a block - its first accumulated sample - until its result is out: it covers the whole two-pass tail as well. Its last high clock is the OUT_DV pulse, and it falls on the clock after. On a continuous stream it simply stays high, because the scan of one block always overlaps the accumulation of the next. Present on the symbol only when Enable BUSY = YES.
INTEGRATING 1 bit
High only while the block is accumulating (pass one): it rises on the clock after the first sample of a block is accepted and falls on the clock after the N-th. It does this for every block, emitted or dropped, so on a continuous stream it marks twice as many boundaries as there are results. BUSY high with this low is pass two. Present on the symbol only when Enable INTEGRATING = YES.
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 end of a block

  • it HOLDS the final count through the tail and past OUT_DV, until the first sample of the next block takes it back to 1, it reads N at OUT_DV only when the input STOPS for the whole tail. On a CONTINUOUS stream the next block has already started by then, so OUT_DV shows how far into it the input has got, not N - the clock that always reads N is the one INTEGRATING falls on. Only RESET clears it to 0. Fixed 32 bits. Present on the symbol only when Enable SAMPLE_COUNT = YES.

Properties

Property window

IN Integer Bits IN_BitsInt

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

Integer bits of the input sample (the sign, when present, uses one of them). 1..64. Default 16. It also sets the WIDTH of every entry of the sample buffer, so it is a memory cost here as well.

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, i.e. the bits to the right of the binary point. Total width = integer + fractional bits, and must not exceed 64.

Fractional bits of the input sample. 0..64. Total input width must be 2..64 bits. Default 0. Like the integer bits, this widens every entry of the sample buffer.

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 is signed (two’s complement) or unsigned.

SIGNED (two’s complement) or UNSIGNED input. Default SIGNED. An UNSIGNED input costs one extra bit internally, because a sample has to be promoted to signed before it can be accumulated - but the buffer stores the raw sample, so it does not cost memory.

Default: SIGNED

Options: UNSIGNED SIGNED

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, so it is capped at 14 instead of the 31 the rest of the family allows.

Largest block-size exponent the block is sized for: the block can be up to $2^{\text{MaxBlockExponent}}$ samples long, and the EXP input is clamped to this value at run time. HERE IT ALSO SIZES A MEMORY: the block keeps $2 \times 2^{\text{MaxBlockExponent}}$ samples in a ping-pong buffer, so it is capped at 14 instead of the 31 the rest of the family allows, and defaults to 10 instead of 20. With a 16 bit input that is about one block RAM at 10, about four at 12 and about sixteen at 14. Set it to the largest block you actually use and no larger - unlike elsewhere in the family, over-provisioning here costs BRAM in proportion. Range 1..14.

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 (the sign, when present, uses one of them).

Integer bits of the MEAN_ABS_DEV output. 1..64, default 16. The value can never exceed the largest deviation in the block, which is bounded by the sample range, so the input’s integer bits are always enough.

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, i.e. the bits to the right of the binary point. Total width = integer + fractional bits, and must not exceed 64.

Fractional bits of the MEAN_ABS_DEV output. 0..64, total width 2..64 bits, default 0. Ask for more than the input has if the spread you are measuring is comparable to an input LSB - the exact integer $T$ carries those bits, they are simply discarded by the final shift if you do not ask for them.

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 is signed (two’s complement) or unsigned.

SIGNED or UNSIGNED MEAN_ABS_DEV output. Default UNSIGNED - an average distance is never negative, and UNSIGNED buys one bit of range for free.

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 BUSY pin exists. It is high from the first sample of a block until its result is out, the whole two-pass tail included, and its last high clock is the OUT_DV pulse. NO: the pin and its register are removed before synthesis. 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 INTEGRATING pin exists. It is high only while the block is accumulating (pass one), so BUSY high with INTEGRATING low means “the samples are all in, I am scanning them” - which on this block is N clocks long. NO: the pin and its register are removed. 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 SAMPLE_COUNT pin exists - a fixed 32 bit count of the samples accumulated so far in the current block, holding the final count through the tail and past OUT_DV. NO: the pin and its counter are removed. 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 when the result has to be requantised into a coarser output format. TRUNCATE: drop the bits (cheaper, adds a negative bias). This is the only rounding in the whole block - the mean itself is never rounded. 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 to the largest representable value of the output format (symmetric bounds for signed formats). NO: wrap around. Only matters when the output format is too narrow for the value - which, for an output sized like the input, cannot happen. Default YES.

Default: YES

Options: NO YES

The half-rate contract, in detail

The two passes are:

pass what happens clocks
one accumulate $S_1 = \sum x_i$ and store every sample N (the block itself)
two read the samples back and accumulate $T = \sum \lvert N x_i - S_1 \rvert$ N
plus one clock to issue the first read, one to load, one to present 3

so the tail is

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

and $L > 2^{\mathrm{EXP}}$ always. There is no exponent at which it fits, which is why the property validator does not look for one: half-rate is this block’s published contract, not a misconfiguration, and the property page will never refuse a configuration on tail grounds.

Worked cadence

Take $\mathrm{EXP} = 10$, so $N = 1024$ and $L = 1027$. Say block $k$’s last sample is accepted on clock $t$:

  • the tail runs from $t$ to $t + 1027$, and OUT_DV pulses on clock $t + 1027$;
  • block $k+1$ finishes on clock $t + 1024$ - the tail still has 3 clocks to go
    • so block $k+1$ is DROPPED: no OUT_DV, and its samples were never handed to the scanner;
  • block $k+2$ finishes on clock $t + 2048$, the tail has been idle since $t + 1027$, so block $k+2$ is emitted.

Result: one result every 2048 samples, i.e. one every $2N$. In general the effective output rate is $f_s / 2N$ and the duty of the answer is 50% of the blocks.

For $\mathrm{EXP} \ge 2$ that alternation is exact. At $\mathrm{EXP} = 1$ ($N = 2$, $L = 5$) two consecutive blocks are dropped and you get one result in three - which is a corner, not a use case; the smallest meaningful exponent is 1 and the smallest useful one is considerably larger.

What a dropped block does NOT do

A dropped block is not an error and leaves nothing behind:

  • IN_DV, the sample counter, the block framing and $S_1$ all behave identically for a dropped block and an emitted one;
  • the samples of a dropped block are still written into the buffer - they are simply never scanned;
  • the next emitted block’s answer is exactly as correct as if nothing had been dropped;
  • there is no overrun pin. OUT_DV is the signal: count OUT_DV pulses, not blocks.

Alternatives if you need a result every block

  • Two of these blocks fed from the same stream with their EXP pins offset. Each covers alternate blocks, and between them no block is missed. It costs two sample buffers, so budget the BRAM.
  • Block Std Dev. One pass, constant memory, no such limitation, and its tail is tens of clocks rather than thousands. Use it unless you specifically need the outlier robustness.
  • Block Mean into Block Mean Abs’s LEVEL pin. If a one-block-old mean is good enough - and on a stationary signal it is - this gives you a one-pass, no-memory, every-block answer that costs nothing extra. This is the recommended alternative.

The sample buffer is PING-PONG

There are two sample buffers of $2^{\text{MaxBlockExponent}}$ entries each, and they swap at every accepted block end. While pass two reads the finished block out of one, the input stream is still arriving and is written into the other, so the reader and the writer never touch the same memory - not for one clock.

A single buffer with the writes gated off during the scan does not work, and that is a verified dead end rather than a hypothetical: the scan overruns the block by three clocks, so the block after the dropped one would lose its first few samples and report a wrong answer with no symptom at all.

What it costs, and why Max Block Exponent stops at 14

The buffers hold $2 \times 2^{\text{MaxBlockExponent}}$ samples. Here the exponent sizes a MEMORY, not just an accumulator width, which is why Max Block Exponent is capped at 14 on this block instead of the 31 the rest of the family allows - and why its default is 10 instead of 20. The HLS core itself refuses to compile above 14.

Max Block Exponent samples buffered with a 16 bit input
10 (default) 2 x 1024 32 kbit, about 1 block RAM
12 2 x 4096 128 kbit, about 4 block RAMs
14 (maximum) 2 x 16384 512 kbit, about 16 block RAMs

Set it to the largest block you actually use and no larger - unlike the rest of the family, where over-provisioning costs a few flip-flops, here it costs block RAM in proportion.

The memory is read synchronously, and the scan is written that way on purpose: the address is issued on one clock and the data used on the next, through an explicit holding register. A BRAM read is not combinational, and pretending otherwise is a known way to lose the first sample of a scan.

THE ANSWER IS BIT EXACT - the mean is never rounded

The obvious implementation would compute the mean, round it into some intermediate format, and subtract that. It would cost up to half an LSB per sample. This block does not do that. Write the mean as $S_1/N$ and clear the denominator:

$$ \mathrm{mad} = \frac{1}{N}\sum_i \left| x_i - \frac{S_1}{N} \right| = \frac{\sum_i \left| N x_i - S_1 \right|}{N^2} $$

and $N x_i$ is a SHIFT, because N is a power of two. So pass two accumulates the exact integer $T = \sum |N x_i - S_1|$, and the only rounding anywhere in the block is the single final requantisation of $T / N^2$ into your Q format.

The host regression (tb/block-ops/run_tb.ps1) demands tolerance ZERO against a Python golden (tb/block-ops/gen_golden.py) that evaluates the definition in exact rational arithmetic and shares no algorithm with the core

  • not “within 1 LSB”, not “within a few counts”. Any deviation at all fails the build.

Accumulation and IN_DV

IN_DV is the only qualifier. It says “this clock carries a sample”: a sample is accumulated, written into the buffer, and counts towards N, exactly on the clocks where IN_DV is high. Clocks with IN_DV low are ignored completely - whatever sits on IN during them cannot corrupt the block or the buffer - while the tail keeps running, which is what you want: pass two is driven by the clock, not by the input stream, so a gapped input does not slow the scan down and does not speed it up.

One consequence worth spelling out: because the scan is clock-driven and the input is IN_DV-driven, a gapped stream makes the drop pattern less regular. With gaps, a block takes more than N clocks of wall time to accumulate, which gives the tail more room; a sparse enough stream can emit every block. The alternation is the worst case, not a fixed law.

Unconnected, IN_DV ties to '1' and EXP ties to 10 (N = 1024), so the block free-runs with nothing wired except IN.

There is deliberately no CE pin. On the all-in-one Block Statistics block an earlier revision had one, and it did not survive synthesis: with nothing but internal state gated by it, Vitis could reason the frozen path away and delete the port from the generated entity while SciCompiler’s wrapper still wired it, which failed a real Vivado build with [VRFC 10-718] formal port <ce> does not exist in entity. The whole per-operator family was built without one. To stall this block, gate its IN_DV - a block that only accumulates on IN_DV has no need to be frozen.

When EXP changes

EXP is clamped to Max Block Exponent and then latched on the first accepted sample of a block, and held for that whole block. A change therefore takes effect on the NEXT block: a block in progress always finishes against the N it was started with, and a block is never emitted against a different N than the one it was accumulated with - which matters here more than anywhere, because the result divides by $N^2$ and the scan reads back exactly N entries.

Because the tail is $2^{\mathrm{EXP}} + 3$, changing EXP changes the latency by a factor of two per step. Going from EXP = 10 to EXP = 11 takes the tail from 1027 to 2051 clocks and the answer interval from 2048 to 4096 samples.

Timing: the latency contract

OUT_DV pulses for one clock when the tail COMPLETES, L clocks after the clock on which the N-th sample of the block was accepted - not when that sample arrives. MEAN_ABS_DEV is updated on that same clock and on no other; it holds the previous emitted result until then, which means it holds it across the dropped block too. The state walk is IDLE -(go)-> LD -> SCAN x N -> FIN -> IDLE with OUT_DV registered, so

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

EXP N L one result every
4 16 19 32 samples
8 256 259 512 samples
10 1024 1027 2048 samples
14 16384 16387 32768 samples

Worked example: with EXP = 10 the N-th sample of a 1024-sample block is accepted on clock $t$; OUT_DV pulses on clock $t + 1027$, and MEAN_ABS_DEV changes on that clock and on no other. The block that finished at $t + 1024$ produced no pulse at all.

Knowing where the block is: BUSY, INTEGRATING and SAMPLE_COUNT

Three optional status outputs, all defaulting to NO. They answer different questions:

INTEGRATING BUSY
accumulating the block 1 1
tail scanning 0 1
idle 0 0

Every output of this block is a register, so each status bit is observed on the clock after the event that sets it:

  • INTEGRATING rises on the clock after the FIRST sample of a block is accepted and falls on the clock after the N-th - it is high exactly while the block is ACCUMULATING, and it does that for every block, dropped or not.
  • BUSY covers the accumulation and the tail. It rises with INTEGRATING, stays high across the tail, and its LAST HIGH CLOCK IS THE OUT_DV PULSE; it falls on the clock after. On a continuous stream it simply never drops, because the scan of one block overlaps the accumulation of the next by construction.
  • On a continuous stream INTEGRATING dips for exactly one clock per block boundary - which makes it a free block marker, and here it marks twice as many boundaries as there are results.
  • SAMPLE_COUNT is a fixed 32 bits and reads 1 after the first accepted sample, N after the N-th. It is NOT cleared at the block end: it HOLDS N through the tail and past OUT_DV, until the first sample of the next block takes it back to 1. so it reads N at OUT_DV only when the input STOPS for the whole tail; on a CONTINUOUS stream the next block has already started and OUT_DV shows how far into it you are. The clock that always reads N is the one INTEGRATING falls on - which is the useful thing to latch alongside the result. Only RESET clears it to 0.

The signal to trust is OUT_DV. BUSY and INTEGRATING describe the block framing, which runs at full rate; only OUT_DV tells you which blocks actually produced a number.

Q formats

Both data ports carry their own fixed point format (integer bits, fractional bits, sign), the same convention as the Fixed P. family. The result is requantised into the MEAN_ABS_DEV format with the selected rounding (nearest / truncate) and overflow policy (saturate / wrap).

Sizing is straightforward:

  • MEAN_ABS_DEV is UNSIGNED by default - an average distance is never negative, and UNSIGNED buys one bit.
  • It can never exceed the largest deviation in the block, which is bounded by the sample range. For a 16 bit signed input the extreme case - half the block at the negative full scale and half at the positive - gives mad = 32767.5, and the default 16 bit unsigned output covers it with room to spare. The input’s integer bits are always enough.
  • Give it more fractional bits than IN if the spread you are measuring is comparable to an input LSB - the exact integer $T$ carries those bits, they are simply discarded by the final shift if you do not ask for them.

The MEAN_ABS_DEV pin’s format properties are named after the core’s internal label, MAD_... - MAD_BitsInt, MAD_BitsFract, MAD_Sign - so look for MAD in the property grid.

Verification

The core is regression tested by a host-side csim harness (tb/block-ops/run_tb.ps1) that runs one simulated clock at a time and follows OUT_DV - which for this block is exactly the point, since the harness has to agree with the core about which blocks are emitted at all. The expected values come from tb/block-ops/gen_golden.py, which evaluates the definition in exact rational arithmetic and shares no algorithm with the core; the tolerance is 0. Coverage includes pseudo-random input (MD_RAND_E5), a constant signal whose mad is exactly zero (MD_CONST_E5), a signal of extreme samples at both full scales that drives the accumulator and the output format to their limits (MD_EXTR_E5), a fractional output format (MD_Q_E5), IN_DV gaps inside the accumulation (MD_DVGAP_E5), an EXP that changes half way through a block (MD_EXPCHG), and an unsigned 8 bit input (MD_U8_E5). The status outputs are checked clock by clock against the contract above, and the tail length is cross-checked at compile time between the C macro, the VB TailClocks function and the golden generator - the testbench refuses to build if the three disagree.