DSP - BLOCK LINEAR REGRESSION
The least-squares straight line fitted to a block of N consecutive samples AGAINST THE SAMPLE INDEX - x[i] = INTERCEPT + SLOPEi. SLOPE is the DRIFT RATE in input units per sample; INTERCEPT is the fitted value at the FIRST sample of the block. What it is for is DRIFT REMOVAL: feed both to a downstream subtracter and the linear trend is gone from the next block, and a drifting baseline is the thing that ruins every other statistic in this family. It also reads out directly as ‘how fast is this detector warming up’. THERE IS NOT ONE MULTIPLIER IN THIS BLOCK, not even a serial one: the regressor is the known sample INDEX, so every sum over it is a closed form in N and therefore a SHIFT, and sum(ix[i]) is reached with TWO CASCADED ACCUMULATORS instead of an index multiply. The only real arithmetic is the two divisions by N(N^2-1) and N(N+1), which genuinely are not powers of two; they share ONE reused restoring-division stage, so L = 3 + (enabled outputs)*CNUMW and the tail does NOT depend on the runtime exponent. Turning an output off removes its division, its pin and its clocks. N is 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. Blocks of up to 2^20 samples out of the box, 2^31 if you ask for it.
Introduction
The Block Linear Regression block chops the input stream into consecutive blocks of N samples and, at the end of each block, publishes the least-squares straight line fitted to that block against the sample index:
$$ x_i ;\approx; \mathrm{INTERCEPT} + \mathrm{SLOPE}\cdot i , \qquad i = 0 \ldots N-1 $$
It is the line that minimises $\sum_i (x_i - \mathrm{INTERCEPT} - \mathrm{SLOPE}\cdot i)^2$ over the block. Two numbers come out:
| output | what it is | units |
|---|---|---|
SLOPE |
the DRIFT RATE of the block | input units per sample index |
INTERCEPT |
the fitted value at the FIRST sample of the block ($i = 0$) | the units of the input |
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 |
What it is FOR: DRIFT REMOVAL
A drifting baseline is the thing that ruins every other statistic in this family. It inflates Block Std Dev and Block Variance (the drift itself becomes “spread”), it hides a real change in Block Mean, it moves the skewness off zero, and it makes Block Threshold Count depend on when you looked rather than on what the signal did.
This block measures the drift so you can take it out. Feed SLOPE and
INTERCEPT to a downstream subtracter - reconstruct
$\mathrm{INTERCEPT} + \mathrm{SLOPE}\cdot i$ with a counter and an
accumulator, or drive a DAC on a baseline-restore loop - and the linear trend
is gone from the next block. Everything downstream then measures the
signal instead of the drift.
(The fit describes the block that has just finished, so applying it to the next block assumes the drift continues at the same rate. That is the usual baseline-restore assumption, and it is a good one for thermal drift, leakage and slow bias movement - the things this block exists for.)
And as a measurement in its own right
SLOPE on its own is a direct “how fast is this detector warming up”
readout: degrees, volts or counts per sample, straight out of a pin, with no
differentiation and no noise amplification. Fitting a line across N samples
averages the noise down instead of amplifying it, which is exactly what a
first difference does not do.
THERE IS NOT ONE MULTIPLIER IN THIS BLOCK
Not one - not even a serial one. That is the headline engineering fact about this block, and it comes from two observations.
1. The regressor is the sample INDEX, which is known. $\sum i$ and $\sum i^2$ are closed forms in N, and N is a power of two, so every one of them is a SHIFT. Written about the centred index $w_i = 2i - (N-1)$ the normal equations collapse to
$$ \mathrm{SLOPE} = \frac{6,S_w}{N(N^2-1)} , \qquad \mathrm{INTERCEPT} = \frac{(N+1)S_x - 3 S_w}{N(N+1)} , \qquad S_w = \sum_i w_i x_i $$
and both denominators are built from N by shifts and one add: $N(N^2-1) = 2^{3E} - 2^{E}$ and $N(N+1) = 2^{2E} + 2^{E}$.
2. $S_w$ itself needs no multiplier either. With $R_i$ the running sum of $x_0 \ldots x_i$ and $A = \sum_i R_i$,
$$ \sum_i i,x_i = N S_x - A , \qquad S_w = (N+1)S_x - 2A $$
so TWO CASCADED ACCUMULATORS replace the index multiply completely - $S_x$ is simply $R$ at the end of the block. The whole per-sample datapath of this block is two adders.
The only real arithmetic left is the two divisions, by $N(N^2-1)$ and $N(N+1)$, which genuinely are not powers of two. They share one reused restoring-division stage.
Cost
Per clock, while accumulating: two adders. No multiplier, no DSP, no sample buffer, no memory.
In the tail: one reused compare-subtract stage, run once per enabled output. Turning an output off really does remove its division, its pin and its clocks - a slope-only placement has half the tail of a two-output one.
When to use this instead of Block Statistics
The all-in-one Block Statistics block is not deprecated, but it does
not fit a line - there is no SLOPE or INTERCEPT pin over there, so for
these numbers there is no choice to make: this block is the only way to get
them.
The usual family rule still applies to everything around it:
- you want several statistics of the SAME block - mean and RMS and min/max of the same N samples - place one Block Statistics block. They share one accumulator set and one serial tail, so the second and third statistic are nearly free.
- you want exactly one number - use the matching per-operator block. Then you synthesise only that number, and nothing else reaches the synthesiser.
A Block Linear Regression block next to a Block Statistics block accumulates the same stream a second time - but here that second accumulation is two adders, which is about as cheap as a duplicate can be.
Pin Description
IN_DV is high. Those two adders are the whole
per-sample datapath of this block.
'1'. (There is
deliberately no CE pin - to stall the block, gate this.)
SLOPE reads 0 and INTERCEPT reads the sample itself.
Unconnected defaults to 10 (N = 1024).
OUT_DV clock and on no other; it holds the previous block’s result
until then. Present on the symbol only when Enable SLOPE = YES.
OUT_DV clock and on no
other; it holds the previous block’s result until then. Present on the
symbol only when Enable INTERCEPT = YES.
SLOPE and INTERCEPT are
both 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 one or two serial
divisions. 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, 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 - the N the slope refers to. 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 widens both numerators, so it adds one clock of tail per bit per enabled output.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. It is taken into account when the quotients are requantised, soSLOPE and INTERCEPT are always in real input units
whatever you set here.
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 - and that bit widens the divider by one step per enabled output. Note that an UNSIGNED input can still have a negative slope and a negative fitted intercept, so keep the output formats signed.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 accumulators are 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 second
accumulator by two bits per unit (it grows as $N^2$) and the two
denominators as well, and it lengthens the tail by two clocks per unit
per enabled output. 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. Keep
it at the largest block you actually use.
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
YES: the the SLOPE output pin is present. NO: the pin AND all of its logic are removed BEFORE synthesis, so nothing is paid for it.
YES: theSLOPE pin is present. NO: the pin, its division by
$N(N^2-1)$ and its CNUMW clocks of tail are all removed BEFORE synthesis,
so nothing is paid for it. Default YES. At least one of SLOPE and
INTERCEPT must be enabled or the property page refuses the settings.
Default: YES
Options: NO YES
YES: the the INTERCEPT output pin is present. NO: the pin AND all of its logic are removed BEFORE synthesis, so nothing is paid for it.
YES: theINTERCEPT pin is present. NO: the pin, its division by
$N(N+1)$ and its CNUMW clocks of tail are all removed BEFORE synthesis.
Default YES. Turning this off is the cheapest way to halve the tail when
you only want the drift rate. At least one of SLOPE and INTERCEPT must
be enabled. Note that the divider is sized at the wider of the two
formats either way, so disabling an output shortens the tail but does not
narrow the divider.
Default: YES
Options: NO YES
Number of INTEGER bits of the SLOPE output (the sign, when present, uses one of them).
Integer bits of the SLOPE output (the sign uses one of them). 1..64, default 8, i.e. up to $\pm 127$ input units of change per sample - a ramp that crosses the full scale of a 16 bit input in about 500 samples. Widen it only if you fit something genuinely that steep.Default: 8
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 SLOPE 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 SLOPE output. 0..64, total width 2..64 bits, default 16. This is the property to get right on this block. The slope is per SAMPLE INDEX, so it is small: a drift of one input LSB across a 1024-sample block is a slope of $2^{-10}$, and with only 8 fractional bits that reads as 0. The default 16 gives an LSB of 1/65536 and resolves such a drift to 64 counts. Each bit costs one clock of tail (through CNUMW), which is cheap for what it buys.Default: 16
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 SLOPE output is signed (two’s complement) or unsigned.
SIGNED or UNSIGNED SLOPE output. Default SIGNED, and it should stay that way - a drift can go either way, and an UNSIGNED format clamps every falling block to 0.Default: SIGNED
Options: UNSIGNED SIGNED
Number of INTEGER bits of the INTERCEPT output (the sign, when present, uses one of them).
Integer bits of the INTERCEPT output (the sign uses one of them). 1..64, default 16 - the same as the default input, which is the right starting point since the intercept has the units of the input. Give it at least the input’s integer bits; a noisy steep fit can extrapolate slightly beyond the range of the block’s own samples.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 INTERCEPT 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 INTERCEPT output. 0..64, total width 2..64 bits, default 8. This is pure resolution and it is worth having: the fitted value at $i = 0$ is effectively an average of N samples, so it is known to far better than one input LSB. Each bit costs one clock of tail through CNUMW - and only if it makes the intercept’s term the widest of the four.Default: 8
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 INTERCEPT output is signed (two’s complement) or unsigned.
SIGNED or UNSIGNED INTERCEPT output. Default SIGNED. Keep it signed for a bipolar input, and also for a unipolar one whose fit can extrapolate below zero.Default: SIGNED
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, 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 divisions 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 a quotient has to be requantised into its output format. TRUNCATE: drop the bits (cheaper, adds a negative bias). It applies on this block, because both quotients are computed with 4 guard bits underneath the requested format precisely so that there is something left to round. 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 each output format (symmetric bounds for signed formats). NO: wrap around. Keep it YES: an under-sizedSLOPE integer field on a steep ramp, or an extrapolated
INTERCEPT, would otherwise wrap and report a large drift as a small one
of the opposite sign. Default YES.
Default: YES
Options: NO YES
The algebra, and how to check it by hand
Per accepted sample the core keeps exactly two registers:
$$ R_i = R_{i-1} + x_i \quad (\text{so } S_x = R_{N-1}) , \qquad A = A + R_i $$
and at the end of the block, in one clock of shifts and adds:
$$ S_w = (N+1)S_x - 2A , \qquad \mathrm{SLOPE} = \frac{6 S_w}{2^{3E} - 2^{E}} , \qquad \mathrm{INTERCEPT} = \frac{(N+1)S_x - 3 S_w}{2^{2E} + 2^{E}} $$
Worth checking on paper once, because it is not obvious that this is a fit at all:
| block | $S_x$ | $A$ | $S_w$ | SLOPE | INTERCEPT |
|---|---|---|---|---|---|
[0, 1, 2, 3] |
6 | 10 | 10 | $60/60 = 1$ | $(30-30)/20 = 0$ |
[1, 3, 5, 7] |
16 | 30 | 20 | $120/60 = 2$ | $(80-60)/20 = 1$ |
[c, c, c, c] |
4c | 10c | 0 | exactly 0 | $20c/20 = c$ |
A clean ramp is an exact fit - the residual is zero and both outputs are exactly right - which is the strongest single check of the algebra, and it is in the regression suite.
N == 1 has no slope
A single point has nothing to fit, and the arithmetic reaches that on its own: $N(N^2-1) = 0$ and the numerator $6 S_w$ is 0 as well, so the zero-denominator path reports SLOPE = 0. The intercept is still perfectly well defined - $N(N+1) = 2$, and it comes out as the sample itself.
No sane configuration asks for EXP = 0, but EXP is a PIN, so this is
reachable at run time and it is defined rather than left to chance.
UNITS: size SLOPE’s fraction generously
This is the one thing to get right on this block.
SLOPE is in input units per SAMPLE INDEX, not per block and not per
second. That makes it a small number: a baseline that drifts by one
input LSB across a whole 1024-sample block has a slope of
$$ \frac{1}{1024} = 2^{-10} $$
So the slope LSB has to be much smaller than that or every slow drift quantises to zero:
| SLOPE fractional bits | slope LSB | one input LSB of drift across 1024 samples reads |
|---|---|---|
| 4 | 1/16 | 0 (0.0156 LSB - lost) |
| 8 | 1/256 | 0.25 LSB - still lost |
| 12 | 1/4096 | 4 LSB - usable |
| 16 (default) | 1/65536 | 64 LSB - comfortable |
Give SLOPE plenty of FRACTIONAL bits. The default is 16, and that is the
right order of magnitude for a drift measurement; do not cut it down to
“match the input” - the input’s format has nothing to do with it.
Its integer bits are the opposite problem and rarely bind: 8 signed bits cover $\pm 127$ input units of change per sample, which is a ramp that traverses the full scale of a 16 bit input in about 500 samples. If you are fitting something that steep, widen them.
INTERCEPT has the units of the input, so size it like the input: at
least the input’s integer bits. Its fractional bits (default 8) are pure
resolution - the fitted value at $i = 0$ is an average of N samples, so it is
known to far better than one input LSB and it is worth asking for the extra
bits. Note that a noisy steep fit can extrapolate the intercept slightly
outside the range of the block’s samples, so keep Saturation on.
Accuracy
Both divisions carry SQG = 4 guard bits underneath the requested Q format,
so the final requantisation still has something to round with, and then the
result is requantised with the selected rounding. Both outputs are therefore
within 1 LSB of the exactly-rounded value, and the host regression
asserts exactly that: tolerance 1 LSB against a Python golden
(tb/block-ops/gen_golden.py) that solves the normal equations in exact
rational arithmetic and shares no algorithm with the core.
Everything before the divisions is exact integer arithmetic: the two accumulators, the closed-form denominators and $S_w$ are all built from shifts and adds with no intermediate rounding at all.
Accumulation and IN_DV
IN_DV is the only qualifier. It says “this clock carries a sample”: a
sample enters the running sum $R$, $R$ enters $A$, and the sample 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 - while the serial tail keeps running, which is what you want: the
tail has nothing to do with the input stream.
Note the consequence for the fit: the index $i$ counts ACCEPTED samples, not
clocks. A gap in IN_DV does not leave a hole in the regressor - the
samples either side of it are consecutive as far as the fit is concerned. If
your IN_DV gaps are irregular and you want a drift rate per unit time
rather than per sample, scale the slope downstream by the average duty cycle.
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, and there is no reason to stall the tail.
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
exponent is what both denominators - $2^{3E}-2^{E}$ and $2^{2E}+2^{E}$ - are
built from, so a block is never divided by the wrong N.
Timing: the serial tail
The state walk
IDLE -(block complete)-> CALC (one clock: all shifts and adds)
-> DIVS x CNUMW (slope) -> DIVI x CNUMW (intercept)
-> FIN -> IDLE
A disabled output’s division state is removed at compile time, not
skipped at run time. OUT_DV pulses for one clock when the tail
COMPLETES - not when the N-th sample arrives - and both data outputs are
updated on that same clock and on no other.
The latency contract
Counted in clocks from the clock on which the N-th sample of the block was accepted:
$$ L = 3 + \bigl(\mathrm{EN_{SLOPE}} + \mathrm{EN_{INTERCEPT}}\bigr) \cdot \mathrm{CNUMW} $$
with the divider width fixed at compile time by
$$ \mathrm{NBW} = \mathrm{IN_SW} + 2,\mathrm{MaxBlockExponent} + 5 , \qquad \mathrm{NAW} = \mathrm{IN_SW} + 2,\mathrm{MaxBlockExponent} + 4 $$
$$ \mathrm{CNUMW} = \max\bigl( \mathrm{NBW} + \mathrm{SLOPE_{fract}} + 4 ,; \mathrm{NAW} + \mathrm{INTERCEPT_{fract}} + 4 ,; \mathrm{SLOPE\ width} + 2 ,; \mathrm{INTERCEPT\ width} + 2 \bigr) $$
where $\mathrm{IN_SW}$ is the input width, plus one bit if the input is UNSIGNED, and the 4 is the guard SQG.
CNUMW has no EXP term: the tail does NOT depend on the runtime exponent. One divider serves both quotients, sized at the wider of the two - which also means that disabling an output halves the tail but does not narrow the divider: a slope-only placement is still sized against the intercept’s format if that one happens to be wider. Max Block Exponent does enter, at two clocks per unit, because it sizes the numerators.
Worked numbers
Take a 16 bit signed input at the default formats (SLOPE Q8.16 signed,
INTERCEPT Q16.8 signed, Max Block Exponent 20, both outputs enabled):
- $\mathrm{IN_SW} = 16$, so $\mathrm{NBW} = 16 + 40 + 5 = 61$ and $\mathrm{NAW} = 60$
- $\mathrm{CNUMW} = \max(61 + 16 + 4,; 60 + 8 + 4,; 24 + 2,; 24 + 2) = \max(81, 72, 26, 26) = 81$
- $L = 3 + 2 \cdot 81 = \mathbf{165}$ clocks, whatever EXP is
- the smallest exponent with $2^{\mathrm{EXP}} \ge 165$ is EXP = 8, i.e. N = 256
Turn the intercept off and the same configuration costs $L = 3 + 81 = 84$ clocks and works from EXP = 7 (N = 128).
| Input | Max Block Exponent | outputs | CNUMW | L | minimum EXP |
|---|---|---|---|---|---|
| 8 bit s | 8 | both | 49 | 101 | 7 (N = 128) |
| 8 bit s | 8 | one | 49 | 52 | 6 (N = 64) |
| 8 bit s | 20 | both | 73 | 149 | 8 (N = 256) |
| 8 bit s | 20 | one | 73 | 76 | 7 (N = 128) |
| 16 bit s | 12 | both | 65 | 133 | 8 (N = 256) |
| 16 bit s | 20 | both | 81 | 165 | 8 (N = 256) |
| 16 bit s | 20 | SLOPE only | 81 | 84 | 7 (N = 128) |
| 16 bit s | 20 | both, SLOPE Q8.24 | 89 | 181 | 8 (N = 256) |
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 $$
A completed block is only handed to the tail when the tail is IDLE, so 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 two accumulators are
unaffected and the following blocks come out correctly, but a result is
silently skipped. There is no error pin and no overrun pin for it.
Two things guard against it, and one hole remains:
- the property page refuses a configuration whose minimum exponent exceeds Max Block Exponent, telling you the tail length and the exponent it would need;
CompileHDLprints both numbers - the tail length and the minimum usable EXP - into the compilation log for every placement;- but
EXPis a PIN. Nothing can stop a design driving it too low at RUN TIME. IfEXPis under software control, clamp it in software to the minimum the compiler printed. A drift measurement wants a long block anyway - fitting a line to 64 samples of a slow drift is mostly fitting the noise.
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 |
| 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, and never during the tail.BUSYhigh withINTEGRATINGlow is precisely the one or two divisions.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 the final count through the tail and pastOUT_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 - which is the useful thing to latch alongside the result, and on this block it is also the N yourSLOPErefers to when you go to convert it into units per second. OnlyRESETclears it to 0.
Q formats
All three data ports carry their own fixed point format (integer bits, fractional bits, sign), the same convention as the Fixed P. family. Each quotient is requantised into its own format with the selected rounding (nearest / truncate) and overflow policy (saturate / wrap); saturation is symmetric for signed formats, as everywhere else in the toolchain.
Both outputs default to SIGNED, and both should stay that way for any real use: a drift can go either way, and a bipolar input can obviously have a negative intercept. See “UNITS” above for how to size them - it is the one place this block will surprise you.
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 solves the normal equations about the centred index in exact rational
arithmetic and shares no algorithm with the core; the tolerance is 1 LSB
on each output. Coverage includes a clean ramp - an exact fit, where the
residual is zero and both outputs are exactly right, which is the strongest
single check of the algebra - a ramp plus noise, a constant block
(slope exactly 0, intercept the constant), pseudo-random input, a sine,
slope-only and intercept-only placements to exercise the removed
division and the changed port list, and a block-to-block alternating EXP
to exercise the per-block latch of the exponent and both denominators. The
tail length is checked at compile time: three copies of the formula exist
- the
BLR_TAILmacro in the core,tail_len()in the generator andTailClocks()in the plugin - and the testbench refuses to build if they disagree. The status outputs are checked clock by clock against the contract above.