DSP - BLOCK MEAN ABS DEVIATION
mean(|x - mean(x)|) over a block of N consecutive samples - the average distance of a sample from the block’s OWN mean. 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. READ THIS 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, so the samples are buffered and read a SECOND time, pass two takes N clocks, the tail is LONGER THAN THE BLOCK ITSELF (L = 2^EXP + 3) and the family’s normal drop rule then applies every other block. For EXP >= 2 the pattern is exactly alternate; everything else is unchanged and OUT_DV tells you which blocks produced an answer. The sample buffer is PING-PONG (2 x 2^MaxBlockExponent samples), which is why Max Block Exponent is capped at 14 here instead of 31. The answer is BIT EXACT because the mean is never rounded: mad = sum|Nx - S1| / N^2 and Nx is a shift. It is about the MEAN, not the MEDIAN, and needs NO SORTING. N is a power of two chosen at RUN TIME on the EXP pin. IN_DV is the only qualifier and there is deliberately no CE pin. Optional BUSY / INTEGRATING / SAMPLE_COUNT status outputs.
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_COUNTkeep their published meaning, andOUT_DVtells 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_DV is
high. Every sample of every block is stored, because pass two has to read
them all back.
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.)
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.
MEAN_ABS_DEV is updated on this clock
and on no other, and BUSY is still high here and falls on the next clock.
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.
BUSY high with this low is pass two. Present on the symbol only
when Enable INTEGRATING = YES.
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 atOUT_DVonly when the input STOPS for the whole tail. On a CONTINUOUS stream the next block has already started by then, soOUT_DVshows how far into it the input has got, not N - the clock that always reads N is the oneINTEGRATINGfalls on. OnlyRESETclears it to 0. Fixed 32 bits. Present on the symbol only when Enable SAMPLE_COUNT = YES.
Properties
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
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
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
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 theEXP 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
Number of INTEGER bits of the MEAN_ABS_DEV output (the sign, when present, uses one of them).
Integer bits of theMEAN_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
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 theMEAN_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
Select whether the MEAN_ABS_DEV output is signed (two’s complement) or unsigned.
SIGNED or UNSIGNEDMEAN_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
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: theBUSY 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
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: theINTEGRATING 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
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: theSAMPLE_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
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
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_DVpulses 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;
- so block $k+1$ is DROPPED: no
- 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_DVis the signal: countOUT_DVpulses, not blocks.
Alternatives if you need a result every block
- Two of these blocks fed from the same stream with their
EXPpins 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
LEVELpin. 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 onIN_DVhas 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:
INTEGRATINGrises 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.BUSYcovers the accumulation and the tail. It rises withINTEGRATING, stays high across the tail, and its LAST HIGH CLOCK IS THEOUT_DVPULSE; 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
INTEGRATINGdips 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_COUNTis 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 pastOUT_DV, until the first sample of the next block takes it back to 1. so it reads N atOUT_DVonly when the input STOPS for the whole tail; on a CONTINUOUS stream the next block has already started andOUT_DVshows how far into it you are. The clock that always reads N is the oneINTEGRATINGfalls on - which is the useful thing to latch alongside the result. OnlyRESETclears 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_DEVis 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
INif 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.