TM
HLS

Introduction

The NCO (TM) block is the time-multiplexed version of the Numerically Controlled Oscillator. It generates multiple independent waveforms in parallel, with each channel having its own frequency, phase, and duty cycle settings.

This block is designed for applications requiring multiple synchronized or independent oscillators, such as multi-channel signal generation, beamforming, or polyphonic synthesis.

Pin Description

PERIOD PERIOD_BITS × TM bit
Waveform periods for all channels (packed). Total width = PERIOD_BITS × TM_FACTOR. Channel 0 in LSBs, channel N-1 in MSBs.
Default: Must be connected
PHASE 16 × TM bit
Phase offsets for all channels (packed, 16 bits per channel). Total width = 16 × TM_FACTOR. Each channel’s phase: 0x0000=0°, 0x4000=90°, 0x8000=180°.
DUTY 16 × TM bit
Duty cycle for all channels (packed, 16 bits per channel). Total width = 16 × TM_FACTOR. Used for TRIANGLE and SQUARE waveforms.
SYMMETRY 16 × TM bit
NOT IMPLEMENTED - reserved. The pin is present on the symbol, sliced per channel and wired through to the IP, but no waveform of any lane reads it: TRIANGLE and SQUARE take their asymmetry from that lane’s DUTY and every other waveform is symmetric by construction. Whatever is connected here has no effect on the output word. Leave it unconnected (it then reads as a constant zero). The pin is kept on the symbol so that existing designs that wire it keep working.
CLK 1 bit
System clock input. All channels operate synchronously on this clock. Default: Board acquisition clock.
RESET 1 bit
Synchronous reset input. Resets all phase accumulators to zero. Default: Global reset.
OUT OUT_BITS × TM bit
Waveform outputs for all channels (packed). Total width = OUTPUT_BITS × TM_FACTOR. Channel 0 in LSBs, channel N-1 in MSBs.

Properties

OutputBits OutputBits
Number of bits per channel output. Range: 8 to 32, default 16. For SINE and COSINE the table is 16 bit whatever this is, so the amplitude is rescaled and never re-tabulated: above 16 the table word is shifted up (the low bits are always zero, there is no interpolation) and below 16 it is shifted down, keeping the sign. That shift is arithmetic, i.e. it rounds towards minus infinity, so at 8 bits a lane swings from +127 to -128 and the negative half of the wave sits exactly one count below the mirror of the positive half. The phase-mapped waveforms take the top bits of their phase word instead, so for them a narrow lane is only a coarser quantisation.
TMFactor TMFactor
Number of time-multiplexed channels. Available values: 2, 4, 8, 16, 32, default 8.
AccumulatorBits AccumulatorBits
Width of each per-channel phase accumulator register. It does NOT change the frequency resolution. Every phase increment is a multiple of 2^(bits-16), so each accumulator is effectively 16-bit and every lane’s frequency grid is f_clk/65536 for any setting; a wider accumulator only costs more area, TM times over. Verified by regression: the same PERIOD stream gives a bit-identical output word at 32 and at 64 bits. It does affect the LATENCY of the TRIANGLE waveform, and only that one: the per-lane slope divider is as wide as the accumulator plus 16 bits, so TRIANGLE costs 73 clock cycles at 32 bits and 106 at 64. Available values: 32, 40, 48, 56, 64, default 48 (32 is the cheapest fully functional choice).
WaveformType WaveformType
Type of waveform to generate (same for all channels). Available values: SINE, COSINE, RAMP, TRIANGLE, SQUARE, SAWTOOTH, default SINE.
OutputSign OutputSign
Output signal format (same for all channels). Available values: UNSIGNED, SIGNED, default SIGNED.
PeriodFormat PeriodFormat
Scaling of each lane’s PERIOD word (and, for FIXED40.24, its width: 64 bits per lane instead of 32). The fractional bits are discarded, not rounded, so none of these formats gives sub-sample period resolution: they only let the block be fed periods that are already carried in rescaled fixed-point words. Available values: UINT32, FIXED16.16, FIXED24.8, FIXED40.24, default UINT32.
LUTSize LUTSize
Sine/Cosine lookup table size (shared by all channels). Available values: 256 (1.41° res), 512 (0.70° res), 1024 (0.35° res), 2048 (0.18° res), 4096 (0.09° res), 8192 (0.04° res), default 1024 (0.35° res).

Usage

Time-Multiplexed Architecture

The NCO_TM processes all channels in parallel using fully unrolled loops. Each channel has:

  • Independent phase accumulator
  • Independent frequency (PERIOD) setting
  • Independent phase offset
  • Independent duty cycle control

All channels share the same waveform type, output resolution, and LUT.


Channel Configuration

The TM factor determines the number of parallel channels:

TM Factor Channels PERIOD Width PHASE Width OUT Width
2 2 64/128 bits 32 bits 2×OUT_BITS
4 4 128/256 bits 64 bits 4×OUT_BITS
8 8 256/512 bits 128 bits 8×OUT_BITS
16 16 512/1024 bits 256 bits 16×OUT_BITS
32 32 1024/2048 bits 512 bits 32×OUT_BITS

The total bus widths are multiplied by the TM factor.


Input Signal Packing

Input signals are packed with channel 0 in the least significant bits:

  PERIOD = [Ch_N-1] [Ch_N-2] ... [Ch_1] [Ch_0]
         MSB                          LSB
  

For example, with TM=4 and PERIOD_BITS=32:

  • Bits [31:0] = Channel 0 period
  • Bits [63:32] = Channel 1 period
  • Bits [95:64] = Channel 2 period
  • Bits [127:96] = Channel 3 period

Output Signal Packing

Output signals follow the same packing convention:

  OUT = [Ch_N-1] [Ch_N-2] ... [Ch_1] [Ch_0]
      MSB                          LSB
  

For example, with TM=4 and OUT_BITS=16:

  • Bits [15:0] = Channel 0 output
  • Bits [31:16] = Channel 1 output
  • Bits [47:32] = Channel 2 output
  • Bits [63:48] = Channel 3 output

Per-Channel Phase Accumulators

Each channel maintains its own phase accumulator that increments independently:

$$ \phi_i[n] = \phi_i[n-1] + \Delta\phi_i \mod 2^N $$

where $i$ is the channel index. This allows each channel to generate a completely independent frequency.


Frequency Control

Each channel’s PERIOD slice is the number of clock cycles in one output period of that channel, so its nominal frequency is

$$ f_{out,i} \simeq \frac{f_{clk}}{\text{PERIOD}_i} $$

All channels operate on the same clock, but each has independent frequency control.

Each lane converts its PERIOD into a phase increment on a fixed 16-bit grid, one turn being 65536 units:

$$ \Delta\phi_i = \left\lfloor \frac{65536}{\text{PERIOD}_i} \right\rfloor \cdot 2^{N-16} $$

so the frequency actually realised on lane $i$ is

$$ f_{out,i} = f_{clk} \cdot \frac{\left\lfloor 65536/\text{PERIOD}_i \right\rfloor}{65536} $$

and it equals $f_{clk}/\text{PERIOD}_i$ exactly only when PERIOD$_i$ divides 65536 (2, 4, 8, 16, 32, …, 1024, …, 65536 - PERIOD = 1 is the one exception, see below, because an increment of a whole turn wraps to zero). Example measured on the block: PERIOD = 20 gives an increment of $\lfloor 65536/20 \rfloor = 3276$ instead of 3276.8, i.e. a realised period of $65536/3276 = 20.005$ samples - right to 0.025 % but drifting by one sample every 4000.


Frequency Resolution

The frequency grid does NOT depend on “Accumulator bits”.

Every increment is a multiple of $2^{N-16}$, so the low $N-16$ bits of each accumulator are permanently zero: whatever “Accumulator bits” is set to, each accumulator behaves as a 16-bit one and every lane’s frequency grid is

$$ \Delta f = \frac{f_{clk}}{65536} $$

i.e. 1.526 kHz at a 100 MHz sample rate, 3.815 kHz at 250 MHz - for every accumulator width. This is verified by regression, not asserted: the same PERIOD stream produces a bit-identical output word with “Accumulator bits” = 32 and with 64 (ci-test cases nco_tm::tm2_freq_grid_acc32_sine / tm2_freq_grid_acc64_sine and the matching RAMP pair). “Accumulator bits” changes the width of the TM accumulator registers only, i.e. the area the block costs (TM × bits of register); it buys no frequency resolution, so 32 is the cheapest fully functional choice – and for TRIANGLE it is also the fastest, because that waveform’s per-lane slope divider is as wide as the accumulator plus 16 bits (see Timing).


PERIOD = 0 and PERIOD = 1 freeze a lane

PERIOD = 0 is illegal and the block substitutes 1 for that lane. PERIOD = 1 means “one sample per output period”, i.e. a whole turn per sample: the increment is $\lfloor 65536/1 \rfloor = 65536$ units, which is exactly one turn and therefore wraps to zero. That lane’s phase stops advancing and its slice of the output word holds the last value it had, while the other lanes keep running.

Expect a frozen lane for PERIOD = 0 and PERIOD = 1, not the fastest possible oscillation. The fastest usable setting is PERIOD = 2 (two samples per period, i.e. Nyquist).


Period Format

The same scaling choice applies to every lane:

Format Bits per lane Written word Integer period used
UINT32 32 period word
FIXED16.16 32 period × 65536 word » 16
FIXED24.8 32 period × 256 word » 8
FIXED40.24 64 period × 16777216 word » 24

The fractional bits are DISCARDED, not rounded. Each lane computes period = PERIOD_i >> frac_bits and uses that integer alone, so the fixed-point formats carry no sub-sample period resolution: they behave exactly like UINT32 (UINT64 for FIXED40.24) fed with the rescaled word, and exist only so that a period already carried in a fixed-point word can be connected directly. A written period of 20.8 samples is used as 20.


Waveform Types

All channels generate the same waveform type (configured at design time):

Waveform Description
SINE Sinusoidal wave using shared LUT
COSINE Cosine wave (sine with 90° phase shift)
RAMP Linear ramp from minimum to maximum
TRIANGLE Triangular wave, rise/fall split set by that channel’s DUTY
SQUARE Square wave with per-channel duty cycle
SAWTOOTH Inverted ramp from maximum to minimum

The SYMMETRY input is not read by any of them - see the SYMMETRY pin below.


Per-Channel Parameters

Each channel can have independent:

  • PERIOD: Waveform frequency
  • PHASE: Phase offset
  • DUTY: Duty cycle (for TRIANGLE/SQUARE)

SYMMETRY is sliced per channel too, but no lane reads it.


Timing

The block is fully pipelined: one new output word - all TM channels at once - on every clock cycle (II = 1), so the aggregate throughput is TM × f_clk samples per second. The input-to-output latency is not a small constant: it is set by the HLS schedule of the pipelined 65536/PERIOD divider, and for TRIANGLE by a second divider on top of it.

Measured on the generated RTL (vitis_hls 2020.2, part xc7a35t, 10 ns target clock, 16-bit lanes, 1024-entry LUT) by stepping PHASE by half a turn and counting the clocks to the first different output word:

Waveform Latency (clock cycles)
SINE, COSINE 23
RAMP, SQUARE, SAWTOOTH 22
TRIANGLE 73 / 82 / 90 / 98 / 106 for “Accumulator bits” 32 / 40 / 48 / 56 / 64

Every figure is exactly one cycle more than the scalar NCO, because this core publishes its output word through an extra register (tNCO_OUT) that the scalar one does not have. The TM factor, the period format and the lane width change none of them. TRIANGLE is the one waveform whose latency grows with “Accumulator bits”: its per-lane slope divider is as wide as the accumulator plus 16 bits, so it costs about 8 more pipeline stages for every 8 accumulator bits. Picking 32 instead of the default 48 therefore saves 17 clock cycles of latency and the area of a wider divider, with no effect whatsoever on the waveform (the extra accumulator bits are always zero). For every other waveform the accumulator width is free.

Treat these as the numbers for the stated conditions: the exact depth is chosen by HLS from the target clock period and the FPGA part, so a faster clock or a different device can shift them by a cycle or two. The symbol badge declares the SINE/COSINE value.

All channels share the same latency, so the phase relationships between lanes are exactly what the PHASE slices ask for - the delay is common to all of them.


Resource Usage

The TM version uses more resources than the single-channel NCO:

  • Phase accumulators: TM × accumulator_bits
  • LUT: Shared (1 copy)
  • DSP blocks: Proportional to TM factor

For minimal resource usage with many channels, consider using a lower TM factor with external time-multiplexing.


Synchronization

All channels are processed synchronously on the same clock edge. This ensures:

  • Zero phase drift between channels
  • Deterministic timing relationships
  • Suitable for coherent multi-channel applications

Resources & Timing

  • Latency: 23 clock cycles for SINE/COSINE, 22 for RAMP/SQUARE/SAWTOOTH, 73 to 106 for TRIANGLE depending on the accumulator width (73 at 32 bits, 90 at the default 48, 106 at 64) – measured on the generated RTL, vitis_hls 2020.2, part xc7a35t, 10 ns clock; one cycle more than the scalar NCO because of the extra output register, and clock- and part-dependent

  • Throughput: TM samples per clock cycle (II=1)

HLS-based implementation with fully unrolled parallel processing. Resource usage scales with TM factor; the accumulator width changes area only, never the frequency resolution.