DSP - BLOCK CREST FACTOR
The crest factor - peak over RMS - of a block of N consecutive samples, with peak = max(|MIN|,|MAX|), the usual definition, so a signal whose NEGATIVE excursion is the larger one reports that excursion. N is a power of two chosen at RUN TIME on the EXP input pin (EXP = 10 means N = 1024), so the /N inside the RMS is an exact arithmetic shift - but the peak/rms ratio is a real division, and this is THE ONLY BLOCK OF THE FAMILY THAT NEEDS ONE. It is the most expensive of the twelve: the serial square root and the serial restoring divider run BACK TO BACK, each one reused compare-subtract stage taking one step per clock, so the cost is CLOCKS, not comparators, and there is no DSP in the tail. The latency is L = 2 + (RTW + 1) + (CNUMW + 1) and 2^EXP >= L must hold or that block’s result is silently dropped. An internal RMS Q format, which is NOT a pin, sizes the denominator and therefore both the accuracy of the quotient and the length of the tail. An all-zero block is a real case and is handled explicitly. IN_DV is the only qualifier and there is deliberately no CE pin. Optional BUSY / INTEGRATING / SAMPLE_COUNT status outputs. Blocks of up to 2^20 samples out of the box, 2^31 if you ask for it.
Introduction
The Block Crest Factor block chops the input stream into consecutive blocks of N samples and, at the end of each block, publishes the crest factor of that block:
$$ \mathrm{peak} = \max\bigl(|\min_i x_i|,\ |\max_i x_i|\bigr), \qquad \mathrm{rms} = \sqrt{\frac{1}{N}\sum_{i=0}^{N-1} x_i^2}, \qquad \mathrm{CREST} = \frac{\mathrm{peak}}{\mathrm{rms}} $$
PEAK IS $\max(|\min|,|\max|)$, NOT MAX alone - that is the usual meaning
of crest factor - so a signal whose negative excursion is the larger one
reports that excursion, and a block that lies entirely below zero still has a
crest factor.
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 | 12 | 4096 |
| 6 | 64 | 16 | 65536 |
| 8 | 256 | 20 | 1048576 |
Because N is a power of two, the division by N inside the RMS is an exact arithmetic shift - it is folded into the radicand of the square root and costs nothing. The peak / rms ratio is the real division, and it is the only one in this family.
What it is FOR
The crest factor is the standard “how peaky is this” number: about 1.41 for a sine, about 1 for a square wave, 1 for a constant, and large for a spiky or pulsed signal. It is scale invariant - it does not change when the signal is amplified - which is exactly what makes it useful as a shape discriminator:
- pile-up and spike detection on a channel whose gain you do not control;
- clipping detection - a clipped signal loses its peaks and its crest factor collapses towards 1;
- pulsed versus continuous classification, and a duty-cycle-like measure for signals with no threshold to speak of;
- machine condition monitoring, where a rising crest factor on a vibration channel is the classic early impact-fault indicator.
Cost
This is the most expensive block of the twelve. Per clock it costs one multiplier for $x^2$ (unavoidable at one sample per clock), one accumulator and two comparators for the running min and max. In the tail it costs one reused compare-subtract stage for the square root and one for the restoring division, plus their numerator / quotient / remainder / denominator registers. Both engines take one step per clock, so what you pay for the tail is clocks, not comparators, and there is no DSP in the tail at all - the only multiplier in the block is the one that squares the samples during accumulation.
When to use this instead of Block Statistics
The all-in-one Block Statistics block is not deprecated and computes this same crest factor among twenty other statistics. The rule is simple:
- you want several statistics of the SAME block - the crest factor 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. That argument is at its strongest here: over there the
square root that the crest factor needs is the same root the
RMSpin uses, and the min/max registers are the same ones theMIN,MAX,PEAK_TO_PEAK,ARGMINandARGMAXoutputs use. - you want exactly one number - use this block. Then you synthesise only that number, and nothing else reaches the synthesiser.
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.
'1'. (There is deliberately no CE pin - to stall the block,
gate this.)
OUT_DV clock
and on no other; it holds the previous block’s result until then.
CREST is updated on this
clock and on no other. BUSY is still high here and falls on the next
clock. A block whose result was dropped for being too short produces no
pulse at all - that is the only symptom.
OUT_DV pulse, and it
falls on the clock after. On a continuous stream it simply stays high.
Present on the symbol only when Enable BUSY = YES.
BUSY high with INTEGRATING low is exactly the root-then-divide
tail. On a continuous stream it dips for one clock per block boundary,
which makes it a free block marker. 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, so on theOUT_DVclock it reads the length of the block being presented. 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.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. Note that it enters the divider geometry through $K$: MORE input fractional bits SHORTEN the divider.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 squared - and that bit widens the divider by one step as well.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 20 covers blocks of up to 1048576 samples.
Largest block-size exponent the accumulator 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. Raising it widens the internal
sum-of-squares register by one bit per unit; unlike on Block Variance it
does not lengthen the tail, because neither engine here has an
exponent term. It must be at least the minimum usable exponent of the
configuration, or the property page refuses the settings. 1..31,
default 20, i.e. blocks of up to 1048576 samples out of the box.
Default: 20
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
Number of INTEGER bits of the CREST output (the sign, when present, uses one of them).
Integer bits of the CREST output. 1..64, default 4. A crest factor is ~1.41 for a sine and ~1 for a square wave, so 4 integer bits (up to 15) is generous for a real signal; raise it only for very spiky data.Default: 4
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 CREST 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 CREST output. 0..64, total width 2..64 bits, default 12 (steps of 1/4096). This is where the resolution goes - but it also enters $K$ and therefore the divider width, so every fractional bit can add a clock to the tail. Do not make it wider than you need.Default: 12
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 CREST output is signed (two’s complement) or unsigned.
SIGNED or UNSIGNED CREST output. Default UNSIGNED - a crest factor is never negative, and UNSIGNED buys one bit.Default: UNSIGNED
Options: UNSIGNED SIGNED
INTEGER bits of the INTERNAL rms, the DENOMINATOR of the crest ratio. This Q format is NOT a pin – the rms itself never leaves the block – but it must be wide enough to hold the largest rms you expect, or the denominator saturates and the reported crest factor is too small. It is always unsigned (an rms is a magnitude), which is why there is no RMS Sign property.
INTEGER bits of the INTERNAL rms, the DENOMINATOR of the crest ratio. 1..64, default 16. This Q format is NOT a pin - the rms itself never leaves the block - but it must be wide enough to hold the largest rms you expect, or the denominator saturates and the reported crest factor comes out too small. It is always unsigned (an rms is a magnitude), which is why there is no RMS Sign property. Each bit adds one clock to the square root, i.e. one clock to the tail.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
FRACTIONAL bits of the INTERNAL rms, i.e. how finely the DENOMINATOR of the crest ratio is resolved. This Q format is NOT a pin. It is the main accuracy / cost trade-off of this block, and it is the EXPENSIVE knob: a fractional bit lands in BOTH serial engines – it widens the square root (one clock) AND it widens the divider through the fixed shift applied to the peak (one more) – so every fractional bit ADDS TWO CLOCKS TO THE TAIL, while an INTEGER bit adds only one. The tail must fit inside the block. Too few and the quotient degrades (the divisor is known only to a coarse LSB); too many and the tail gets long for nothing.
FRACTIONAL bits of the INTERNAL rms, i.e. how finely the DENOMINATOR of the crest ratio is resolved. 0..64, total RMS width 2..64 bits, default 0. This Q format is NOT a pin. It is the main accuracy / cost trade-off of this block: too few and the quotient degrades (the divisor is known only to a coarse LSB, and a small rms can truncate to zero and force saturation); too many and the tail gets long for nothing. Each fractional bit lengthens BOTH the square root and the divider, so it costs two clocks of tail - and the tail must fit inside the block.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
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, 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, so BUSY high with INTEGRATING low means “the samples are
all in, the root and the divider are running”. 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 ratio has to be requantised into a coarser output format. TRUNCATE: drop the bits (cheaper, adds a negative bias). It applies to the final requantisation only - the quotient itself is always floored by the restoring divider. 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 CREST format (symmetric bounds for signed formats). NO: wrap around. The tworms == 0 cases ignore this property: a zero rms with a non-zero peak
always reports the format maximum, and an all-zero block always reports 0,
because neither is a requantisation overflow. Default YES.
Default: YES
Options: NO YES
Timing: the serial tail
All post-accumulation arithmetic is serial - the block has a whole block period of slack after the N-th sample, so there is no reason to build a parallel datapath for it. The state walk is
IDLE -(block complete)-> LD -> SQRT x RTW -> CLD -> DIV x CNUMW -> FIN -> IDLE
the square root and the divider run back to back, each one reused
compare-subtract stage taking one step per clock. OUT_DV pulses for one
clock when the tail COMPLETES - not when the N-th sample arrives - and
CREST is updated on that same clock and on no other. The latency from the
N-th accepted sample to the OUT_DV pulse, counted in clocks, is
$$ L = 2 + (\mathrm{RTW} + 1) + (\mathrm{CNUMW} + 1) $$
with the two engine widths fixed at compile time by your formats:
$$ \mathrm{RTW} = \mathrm{RMS_{int}} + \mathrm{RMS_{fract}} + 5 $$
$$ K = \mathrm{CREST_{fract}} - \mathrm{IN_{fract}} + \mathrm{RMS_{fract}} + 4, \qquad \mathrm{CNUMW} = \max\bigl(\mathrm{IN_SW} + 1 + \max(K, 0),; \mathrm{CREST\ width} + 2\bigr) $$
where $\mathrm{IN_SW}$ is the input width, plus one bit if the input is UNSIGNED (a sample has to be promoted to signed first).
Note what is absent: CNUMW has no exponent term. $K$ is built only from Q
formats, so the division always takes the same number of steps whatever N
is, and $L$ does not depend on EXP at all. Only the value being divided
changes with the block length.
Worked numbers
For an 8 bit signed input at the default formats (CREST Q4.12
unsigned, RMS internal Q16.0):
- $\mathrm{RTW} = 16 + 0 + 5 = 21$
- $K = 12 - 0 + 0 + 4 = 16$, and $\mathrm{IN_SW} + 1 = 9$, so $\mathrm{CNUMW} = \max(9 + 16,\ 16 + 2) = 25$
- $L = 2 + (21 + 1) + (25 + 1) = \mathbf{50}$ clocks
- the smallest exponent with $2^{\mathrm{EXP}} \ge 50$ is EXP = 6, i.e. N = 64
A few more configurations, to show where the clocks go:
| Input | CREST | RMS internal | RTW | CNUMW | L | minimum EXP |
|---|---|---|---|---|---|---|
| 8 bit s | Q4.12 | Q16.0 | 21 | 25 | 50 | 6 (N = 64) |
| 8 bit s | Q4.12 | Q16.4 | 25 | 29 | 58 | 6 (N = 64) |
| 16 bit s | Q4.12 | Q16.0 | 21 | 33 | 58 | 6 (N = 64) |
| 16 bit s | Q4.12 | Q16.8 | 29 | 41 | 74 | 7 (N = 128) |
The 2^EXP >= L rule, and what happens when it is broken
The tail of one block must finish before the next block completes:
$$ 2^{\mathrm{EXP}} \ge L $$
If a block completes while the previous tail is still running, that block’s
result is DROPPED: there is no OUT_DV for it, the accumulators and the
min/max registers are unaffected and the following blocks come out correctly,
but a result is silently skipped. There is no error pin for it.
The property page refuses a configuration whose minimum exponent exceeds
Max Block Exponent, and CompileHDL prints both numbers - the tail
length and the minimum usable EXP - in the compilation log, so the static
half of the problem is caught for you.
But EXP is a PIN. Nothing can stop a design driving it too low at RUN
TIME, and this is the block where that bites hardest: with L in the tens of
clocks, an EXP of 4 or 5 that would be perfectly reasonable on Block Mean
drops every result here, with no indication other than a silent OUT_DV.
If EXP is under software control, clamp it in software to the minimum the
compiler printed.
The RMS internal Q format
The crest factor divides by the RMS, and that RMS comes out of the same digit recurrence Block RMS uses. It is not a pin here - only the ratio leaves the block - but its Q format is still a real design decision, because it sizes the denominator:
- accuracy of the quotient. The divisor is known only to one LSB of this format. Too few fractional bits and the quotient degrades, especially for small RMS values.
- length of the serial tail. $\mathrm{RTW} = \mathrm{RMS\ width} + 5$, one clock per bit, and $\mathrm{RMS_{fract}}$ also enters $K$ and therefore CNUMW - so each fractional bit you add costs two clocks of tail. Too many and the tail gets long for nothing.
- range. RMS Internal Integer Bits must be wide enough to hold the largest RMS you expect, or the denominator saturates and the reported crest factor comes out too small.
There is deliberately no “RMS Sign” property: the RMS is an internal magnitude, always unsigned, and it never leaves the block.
Accuracy
The divider divides by the root, which is itself accurate to about 1 LSB
and biased low, and the quotient is floored. The relative error of
CREST is therefore about one part in the root - a fraction of a percent
for any signal with a meaningful RMS, and worse as the RMS approaches one root
LSB. This is the one compounded error in the family; every other per-operator
block is either bit exact or within 1 LSB.
Because that bound depends on the value, the host regression does not assert a fixed number of LSBs: the golden generator derives a per block tolerance from the published RMS accuracy and checks the quotient against it.
When the RMS is zero
rms == 0 is a real case - an all-zero block. Then:
| peak | rms | CREST reports |
|---|---|---|
| > 0 | 0 | the largest value the CREST format can represent (the ratio is unbounded) |
| 0 | 0 | 0 (a flat zero signal has no crest) |
Both are FORCED explicitly, independently of the Saturation property, because neither is a requantisation overflow - they are the definition of the ratio breaking down.
Note that “rms == 0” here means the internal root truncated to zero, which a long block of very small samples can reach even though the samples are not all zero. That still saturates, and that is still the right answer: the denominator really is below one LSB of the RMS format you chose. If you see saturation on a signal that is not silent, add fractional bits to the RMS internal format - and pay the two clocks per bit.
Accumulation and IN_DV
IN_DV is the only qualifier. It says “this clock carries a sample”: a
sample is squared, accumulated, compared against the running min and max, 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, and in particular cannot fake a peak - while the serial
tail keeps running, which is what you want: the tail has nothing to do with
the input stream.
Unconnected, IN_DV ties to '1' and EXP ties to 10 (N = 1024), so the
block free-runs with nothing wired except IN - though at N = 1024 the
tail hides comfortably, which is not true of every value you might drive.
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- and note that there would be no reason to stall the tail anyway.
There is no LEVEL pin either: the crest factor is referred to zero by
definition.
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. The latched
value is what the radicand is shifted by, so the RMS always refers to the
right block length. Remember that EXP also has to satisfy
$2^{\mathrm{EXP}} \ge L$ at run time, every time.
Knowing where the block is: BUSY, INTEGRATING and SAMPLE_COUNT
Three optional status outputs, all defaulting to NO. They answer different questions, and on this block the difference is at its most useful, because the tail is long:
INTEGRATING |
BUSY |
|
|---|---|---|
| accumulating the block | 1 | 1 |
| serial tail computing | 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.BUSYhigh withINTEGRATINGlow is precisely the root-then-divide tail.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 the next block starts before the previous tail
ends, so
BUSYnever drops andINTEGRATINGdips for exactly one clock per block boundary - which makes it a free block marker. 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 on theOUT_DVclock it reads the length of the block being presented - which is the useful thing to latch alongside the result, and here also the cheapest way to confirm at run time that the block was long enough for the tail. OnlyRESETclears it to 0.
Q formats
IN and CREST carry their own fixed point format (integer bits, fractional
bits, sign), the same convention as the Fixed P. family; the RMS internal
format is a third one that is not a pin. The ratio is requantised into the
CREST format with the selected rounding (nearest / truncate) and overflow
policy (saturate / wrap); saturation is symmetric for signed formats, as
everywhere else in the toolchain - except for the two forced rms == 0 cases
above, which ignore the Saturation property.
Sizing hints:
- a crest factor is ~1.41 for a sine and ~1 for a square wave, so the default 4 integer bits (up to 15) is generous for a real signal; raise it only for genuinely spiky data;
- the fractional bits are where the resolution goes - the default 12 give steps of 1/4096 - but they also enter $K$ and therefore the divider width, so every fractional bit can cost a clock of tail;
CRESTis never negative, so UNSIGNED buys one bit.
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. The expected values come from tb/block-ops/gen_golden.py,
which evaluates the definitions above in exact rational arithmetic and shares
no algorithm with the core, and which derives a per block tolerance from
the published RMS accuracy rather than asserting a fixed LSB count. Coverage
includes pseudo-random and sinusoidal inputs, a constant signal (crest
factor 1), an all-zero block (rms == 0, the forced 0 case), a
configuration whose root truncates to zero (the forced saturation case) and
maximum positive / maximum negative inputs including an asymmetric signal
whose negative excursion is the larger one - which is what pins
$\mathrm{peak} = \max(|\min|,|\max|)$. The tail length is checked against the
formula at compile time: three copies of it exist - the BCR_TAIL macro
in the core, tail_len() in the generator and TailClocks() in the plugin -
and the testbench refuses to build if they disagree. The status outputs are
checked clock by clock against the contract above. A cross-check compiles
this core and the all-in-one block_stats.cpp into the same binary, drives
them with identical stimulus, and compares the two clock by clock.