Xilinx
HLS
Block Preview

Introduction

The Sequential FIR (programmable) block is the run-time reloadable sibling of Component_SeqFIR. It shares the exact same time-shared MAC datapath — one multiplier walks all NumTaps taps within the SysClk / DataClk window, so a long filter costs a single multiplier per channel — but instead of a compile-time coefficient ROM it stores the taps in a small dual-port BRAM that can be rewritten while the design runs.

      SAMPLE_IN ┐  (1 pulse per new sample, gapped every ClockRatio)
    IN_I / IN ┤ [ 1x time-shared MAC over NumTaps ] ── OUT_I / OUT
    IN_Q      ┘        ▲   coef_ram (BRAM)          └─ OUT_Q
                       │                          VALID_OUT ──
    COEF_ADDR ─────────┤
    COEF_DATA ─────────┤  (write h[addr] = data on COEF_WR pulse)
    COEF_WR   ─────────┘
  

FIR Designer

Unlike the fixed seq_fir, this block is configured through the standard property grid (widths, tap count, clock ratio and the MAC output shift). The FIR Designer shown above is still the recommended companion tool: use it to prototype the response and export a quantised integer coefficient set, then stream those coefficients into the block at run time over the COEF_* interface. See the Visual designer note below.

For the fixed-coefficient (ROM, no reload) version see Component_SeqFIR (seq_fir).

Pin Description

IN_I Input InputSize bit BIT VECTOR
In-phase (I) input sample (Complex mode). Signed, Input Bit Width bits.
Default: Must be connected
IN_Q Input InputSize bit BIT VECTOR
Quadrature (Q) input sample (Complex mode). Signed, Input Bit Width bits. Shares the coefficient BRAM with I.
Default: Must be connected
SAMPLE_IN Input 1 bit BIT
New-sample strobe. Pulse high for one SysClk cycle each time a new input sample is valid. Must fire no more often than once every NumTaps cycles (and typically once every ClockRatio cycles).
Default: Must be connected
COEF_ADDR Input ceil(log2(NumTaps)) bit BIT VECTOR
Coefficient write address = tap index, ceil(log2(NumTaps)) bits.
COEF_DATA Input CoefSize bit BIT VECTOR
Coefficient write data, signed Coefficient Bit Width bits. Latched into coef_ram[COEF_ADDR] on a COEF_WR pulse.
COEF_WR Input 1 bit BIT
Coefficient write strobe. When high on a rising CLK edge, COEF_DATA is written to tap COEF_ADDR. Hold low when not programming.
CLK Input 1 bit BIT
System clock input (SysClk). Default: Acquisition clock.
Default: Default Board Clock
RESET Input 1 bit BIT
HLS synchronous reset (ap_rst). Default: Global reset. Clears the delay line, accumulator and counter — but not the coefficient BRAM.
Default: Default Board Reset
OUT_I Output InputSize + CoefSize + 8 - CoefShift bit BIT VECTOR
In-phase (I) filtered output (Complex mode). Signed, InputSize + CoefSize + 8 - CoefShift bits.
OUT_Q Output InputSize + CoefSize + 8 - CoefShift bit BIT VECTOR
Quadrature (Q) filtered output (Complex mode). Signed, InputSize + CoefSize + 8 - CoefShift bits.
VALID_OUT Output 1 bit BIT
Output-valid strobe. Pulses high for one SysClk cycle when a new filtered result is presented, i.e. after the MAC sweep over all NumTaps taps completes.
IN InputSize bit
Real-mode input sample (present only when Signal Type = Real). Signed, Input Bit Width bits.
Default: Must be connected
OUT InputSize + CoefSize + 8 - CoefShift bit
Real-mode filtered output (Real mode). Signed, InputSize + CoefSize + 8 - CoefShift bits.

Properties

Property window

Signal Type SignalType

Real (single channel) or Complex (I/Q with shared coefficients).

Real (single channel, IN / OUT, one multiplier) or Complex (I/Q on IN_I/IN_Q and OUT_I/OUT_Q sharing one coefficient BRAM, two multipliers). Default Complex.

Default: Complex

Options: Real Complex

Input Bit Width InputSize

Bit width of the input sample(s) (signed).

Bit width of each signed input sample. Range 4 to 32, default 16.

Default: 16

Options: 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

Coefficient Bit Width CoefSize

Bit width of each coefficient (signed). Sets COEF_DATA bus width.

Bit width of each signed coefficient. One of 10, 12, 14, 16, 18, 20, 24. Also sets the COEF_DATA bus width. Default 18.

Default: 18

Options: 10 12 14 16 18 20 24

Number of Taps NumTaps

Filter order + 1. Must be <= ClockRatio. Sets COEF_ADDR bus width.

Number of filter taps (filter order = NumTaps - 1). One of 8, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024. Also sets the coefficient BRAM depth and the COEF_ADDR bus width (ceil(log2(NumTaps))). Must be <= ClockRatio. Default 64.

Default: 64

Options: 8 16 24 32 48 64 96 128 192 256 384 512 768 1024

SysClk / DataClk ClockRatio

System-clock cycles per input sample. Must be >= NumTaps.

SysClk / DataClk: system-clock cycles between input samples (the MAC-sweep window). One of 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096. Must be >= NumTaps. Default 128.

Default: 128

Options: 8 16 32 64 128 256 512 1024 2048 4096

MAC Output Shift CoefShift

Right-shift applied to the accumulator (must match the shift used by the driver when quantising the coefficients).

Right-shift applied to the accumulator (MAC output shift). Must match the fixed-point scale the driver used when quantising the coefficients it writes. For a unity-DC-gain scale of 2^(CoefSize-1)-1 use CoefSize - 1. Range 0 to 32, default 17.

Default: 17

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

Usage

Time-sharing / clock-ratio concept

Identical to seq_fir: CLK is the system clock, a new input sample is presented once every ClockRatio system clocks and marked with a one-cycle SAMPLE_IN pulse. On that pulse the delay line shifts, the accumulator clears and a counter starts; each subsequent system clock performs exactly one MAC (acc += delay[cnt] * coef_ram[cnt]); after NumTaps cycles the shifted result is driven on the output and VALID_OUT pulses. Because one tap is processed per clock:

      NumTaps <= ClockRatio          (checked at compile time)
  

so the core needs only one multiplier (Real) or two (Complex), independent of filter length.

Coefficient reload

The coefficients are held in a NUM_TAPS x CoefSize dual-port BRAM (ram_2p) with a synchronous write port:

      on rising CLK:
      if COEF_WR = 1:  coef_ram[COEF_ADDR] <= COEF_DATA   (signed)
  
  • COEF_ADDR is ceil(log2(NumTaps)) bits — the tap index to write.
  • COEF_DATA is CoefSize signed bits — the new coefficient value.
  • COEF_WR is a one-cycle write strobe.

Writes and MAC reads are independent (true dual-port), so you may reload taps at any time. The clean recipe is to update the whole tap set in the gap between samples (the COEF_* interface is normally quiet during a MAC burst); as long as you do not overwrite the tap the MAC is currently reading, the filter transitions smoothly. To load a full NumTaps-length response, issue NumTaps writes walking COEF_ADDR from 0 to NumTaps - 1. There is no double-buffer: the change takes effect on the next convolution that reads the rewritten location.

Coefficients survive reset (the BRAM is not cleared by ap_rst; it powers up to 0 and holds whatever was last written).

Bit widths and scaling

Data is signed two’s complement. Internally:

      ACC_SIZE = InputSize + CoefSize + 8            (8 guard bits)
    OUT_SIZE = ACC_SIZE - CoefShift
             = InputSize + CoefSize + 8 - CoefShift
    ADDR_SIZE = ceil(log2(NumTaps))
  

CoefShift is the right-shift applied to the accumulator before the output. It is a property here (not stored by a designer), and it must match the fixed-point scale that the host/driver used when quantising the coefficients it writes. If you quantise with a scale of 2^(CoefSize-1)-1 (unity DC gain), use CoefShift = CoefSize - 1.

Complex vs real

  • Real: pins IN / OUT, one multiplier.
  • Complex: IN_I/IN_Q and OUT_I/OUT_Q, two multipliers, sharing the same coefficient BRAM (one write port updates both channels).

Visual designer

This block itself uses the standard property grid — double-clicking it edits the hardware parameters directly, it does not open the WebView2 designer. The FIR Designer pictured in the introduction (the same tool the fixed seq_fir opens on double-click) is nonetheless the easiest way to produce a valid coefficient set: design the response there, let it quantise the taps to signed CoefSize integers and note the accumulator shift it reports, then set CoefShift to that value and stream the integer taps into the BRAM over COEF_ADDR / COEF_DATA / COEF_WR (typically from a register bank the SDK fills). Because the taps are not baked in, you can swap the entire response without resynthesising the bitstream.

Latency and throughput

  • #pragma HLS PIPELINE II=1: one MAC per system clock.
  • End-to-end latency from SAMPLE_IN to VALID_OUT is about NumTaps system clocks.
  • Sustained throughput is one filtered sample every ClockRatio system clocks (the input DataClk rate).
  • All ports use the ap_none interface.

Reset

RESET (ap_rst) clears the delay line, accumulator and tap counter. It does not clear the coefficient BRAM.

Typical applications

  • Adaptive / LMS equaliser where an external engine updates taps.
  • Software-defined channel-select filter: change bandwidth from the SDK without a rebuild.
  • Matched filter whose shape tracks a run-time-selected symbol rate.

Resources & Timing

  • Latency: ~NumTaps system-clock cycles from SAMPLE_IN to VALID_OUT

  • Throughput: One sample every ClockRatio system clocks (the DataClk rate)

Implemented with Vitis HLS. One DSP multiplier for Real, two for Complex, regardless of filter length (time-shared MAC, one MAC per system clock, II=1). Coefficients live in a NUM_TAPS x CoefSize dual-port BRAM (ram_2p) with a synchronous write port, so they are reloadable at run time and are NOT optimised away by Vivado (DSP usage is deterministic). A NumTaps-deep delay line (per channel) is kept in registers / SRL. Hard constraint enforced at compile: NumTaps <= ClockRatio.