Xilinx
HLS
Block Preview

Introduction

The block outputs the median of the last WindowSize samples: it sorts the window and returns the middle value,

$$ \mathrm{OUT}(n) = \operatorname{median}\bigl(x(n),, x(n-1),, \ldots,, x(n-W+1)\bigr) $$

with $W$ odd, so the middle sample is unambiguous.

Why a median and not an average

A moving average spreads every disturbance over the whole window: one sample that is wildly wrong drags the output for $W$ samples, and every edge in the signal is smeared into a ramp. The median does neither. As long as fewer than half of the samples in the window are corrupted, the outlier is discarded outright – it never contributes to the result – and a step edge is reproduced as a step, with no overshoot and no ringing.

The three traces above are, from the bottom: the raw input, the same signal through the Mean Filter, and through the Median Filter. Both remove the baseline noise, but look at the pulse. The median keeps the leading edge as steep as it was and the peak at its true height; the mean rounds the peak off, lowers it and spreads it over the window. If the pulse height or its arrival time is what you are measuring, that difference is the whole story.

This makes it the natural first stage against impulsive noise: single sample glitches, pickup spikes, ADC bit errors, and the isolated hits that would otherwise trigger a downstream discriminator.

The price is that it is non linear: it has no frequency response, it cannot be cascaded or analysed like a FIR, and it slightly clips very short genuine pulses. A real pulse narrower than $\lceil W/2 \rceil$ samples is treated as an outlier and removed, so the window must stay shorter than the shortest feature you want to keep.

Pin Description

IN Input Variable bit BIT VECTOR
Input sample, format Q(IN Integer Bits . IN Fractional Bits). Sampled on the rising edge of CLK when IN_DV is high.
Default: Must be connected
IN_DV Input 1 bit BIT
Input data valid, active high. The window advances only on cycles where it is high; tie it to ‘1’ for a free running stream.
OUT Output 16 bit BIT VECTOR
Median of the current window, format Q(OUT Integer Bits . OUT Fractional Bits). Always one of the samples present in the window, never an interpolated value.
OUT_DV Output 1 bit BIT
Output data valid: IN_DV delayed by PipelineLength + 1 clock cycles, i.e. aligned with the sample on OUT.
CLK
Processing clock, connected to the acquisition clock.
RESET
Reset, active high: clears the window to zero and restarts the filter from its zero padded state.

Properties

Property window

Window Size WindowSize

Number of samples in the sliding window. Odd values only, so the middle sample is unambiguous. Cost grows with the SQUARE of the window, so prefer the smallest window that removes your spikes.

Number of samples in the sliding window: 3, 5, 7, 9, 11, 13 or 15. Odd only, so the middle sample is unambiguous.

A window of $W$ rejects any burst of up to $\lfloor W/2 \rfloor$ consecutive corrupted samples, but also removes genuine pulses shorter than that. Cost grows with $W^2$.

Default: 5

Options: 3 5 7 9 11 13 15

IN Integer Bits IN_BitsInt

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

Number of INTEGER bits of the input (1 to 64). When the input is SIGNED, one of these bits carries the sign.

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

Number of FRACTIONAL bits of the input (0 to 64), i.e. the bits to the right of the binary point. Total input width = integer + fractional bits.

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

Arithmetic type of the input:

  • SIGNED – two’s complement
  • UNSIGNED – non negative only

Default: SIGNED

Options: UNSIGNED SIGNED

OUT Integer Bits OUT_BitsInt

Number of INTEGER bits of the output.

Number of INTEGER bits of the output (1 to 64).

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

OUT Fractional Bits OUT_BitsFract

Number of FRACTIONAL bits of the output. Set it equal to the input to keep the result exact; use more bits only if you widen the format elsewhere.

Number of FRACTIONAL bits of the output (0 to 64). Set it equal to the input to keep the result exact – the median is always one of the input samples, so nothing is lost.

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

OUT Sign OUT_Sign

Select whether the output is signed or unsigned.

Arithmetic type of the output, SIGNED or UNSIGNED.

Default: SIGNED

Options: UNSIGNED SIGNED

Rounding Rounding

ROUND: round to nearest when the output has fewer fractional bits than the input. TRUNCATE: drop them (cheaper, adds a negative bias).

Only relevant when the output has fewer fractional bits than the input:

  • ROUND – round to nearest
  • TRUNCATE – discard the extra bits (cheaper, introduces a negative bias)

Default: ROUND

Options: TRUNCATE ROUND

Saturation EnableSaturation

YES: clip to the largest representable output value. NO: wrap around.

Only relevant when the output format cannot represent the selected sample:

  • YES – clip to the largest representable value (symmetric, min = -max)
  • NO – wrap around modulo the output width

Default: YES

Options: NO YES

Pipeline Length PipelineLength

Number of output register stages, i.e. extra latency in clock cycles. 0 takes the result straight out of the combinational logic.

Number of output register stages (0 to 8). Total latency is this value PLUS ONE, because the sliding window register is itself a clock. Does not affect the result.

Default: 1

Options: 0 1 2 3 4 5 6 7 8

Functional description

A shift register holds the last WindowSize samples. Every sample is compared against every other one to compute its rank inside the window, and the sample whose rank is the middle one is sent to the output. Ties are broken by position, so the ranks are always a permutation of $0 \ldots W-1$ and exactly one sample carries the median rank – repeated values are handled correctly.

The window advances only when IN_DV is high, so the filter works on gated or decimated streams as well as on a free running one.

Startup and reset

Reset clears the window to zero. The first $W-1$ outputs after a reset therefore see a window that is still partly filled with zeros, exactly like the initial state of a FIR filter. The output is fully meaningful from the $W$-th valid sample onwards.

Fixed-point format

Input and output carry their own Q format. A value with $N_{int}$ integer bits and $N_{frac}$ fractional bits occupies $N_{int} + N_{frac}$ bits and represents

$$ \text{value} = \frac{\text{raw integer}}{2^{N_{frac}}} $$

When the port is SIGNED one of the integer bits carries the sign (two’s complement).

Because the median is always one of the input samples, the result is exact whenever the output format is at least as wide as the input one, and the Rounding and Saturation properties then have no effect at all. They only come into play if you deliberately narrow the format on the way out.

Resource cost

The rank computation costs $W^2$ comparators, so the cost grows with the square of the window:

WindowSize comparators typical use
3 9 single sample glitches
5 25 the usual default
7 49 noisier channels
9 .. 15 81 .. 225 heavy impulsive noise, slow signals

Use the smallest window that removes your spikes. No DSP slice and no block RAM are used; the shift register is built from flip-flops.

The comparison network is combinational. PipelineLength fixes the latency of the block, and the high level synthesis scheduler distributes the network over that many cycles – so on a wide input and a large window, raising it is the first thing to try if timing closure fails; reducing the window is the second.

Implementation

The block is generated with Vitis HLS from a shared parametric core (Resources/Code/window_filter.cpp), which also serves the Mean Filter: one source, one requantiser, two reductions selected at synthesis time. Every property takes part in the IP identity, so two placements with different settings get their own core and never interfere. A Vitis HLS installation (or a remote build) is therefore required to compile a design containing this block.

Latency

Total latency is PipelineLength + 1 clock cycles: the sliding window is itself a register, and PipelineLength adds that many further output stages on top of it. With PipelineLength = 0 the result comes straight out of the comparison network, one clock after the sample was consumed.

OUT_DV is delayed by exactly the same amount, so the flag always travels with the sample it describes – this matters when IN_DV is gated rather than tied high.

Typical use cases

  • Removing pickup spikes and single sample glitches ahead of a trigger or a discriminator
  • Cleaning a baseline without rounding off the leading edge of real pulses
  • Suppressing isolated ADC bit errors
  • Pre-conditioning a slow signal (temperature, bias, rate) against outliers