AXI TDD

The AXI TDD (Time Division Duplexing) is an FPGA IP core driver that provides a waveform generator capable of addressing RF applications which require Time Division Duplexing, as well as controlling other modules through its dedicated output channels. It solves the synchronization issue when transmitting and receiving multiple frames of data through multiple buffers.

The driver implements:

  • Up to 32 independent output channels with configurable on/off timing

  • Per-channel enable and polarity control

  • Configurable frame length and burst count

  • Initial startup delay before waveform generation

  • Three synchronization sources: external, internal, and software-generated

  • Timing values in both raw clock cycles and milliseconds

  • Dynamic clock rate tracking via clock notifiers

Supported IP Cores

  • AXI TDD IP v2.0.a

Status

Source

Mainlined?

drivers/misc/adi-axi-tdd.c

No

Files

Overview

The AXI TDD controller is a generic waveform generator developed for use in Analog Devices HDL reference designs. Unlike previous TDD engines, this core operates only in standalone mode and is not embedded into other devices.

Each output channel has independently configurable on and off times relative to the start of a frame. The controller generates repeating frames of a configurable length, with an optional startup delay before the first frame. Multiple frames can be grouped into a burst, or the controller can run continuously until disabled.

Synchronization can be triggered from three sources:

  • Software sync: A one-shot trigger via the sync_soft attribute

  • Internal sync: A periodic trigger generated by an internal counter with a configurable period

  • External sync: A trigger from an external signal, optionally synchronized into the core clock domain via a CDC (Clock Domain Crossing) circuit

Adding Linux driver support

Enabling the driver

Configure kernel with make menuconfig:

Linux Kernel Configuration
    Device Drivers  --->
        Misc devices  --->
            <*>   Analog Devices TDD Engine support

Adding a device tree entry

Required properties

  • compatible: Must be "adi,axi-tdd"

  • reg: Physical base address and size of the IP core registers

  • clocks: References to the system clock and the TDD core clock

  • clock-names: Must be "s_axi_aclk", "intf_clk"

Device tree example

Basic TDD core instance:

tdd@84a00000 {
    compatible = "adi,axi-tdd";
    reg = <0x84a00000 0x10000>;
    clocks = <&zynqmp_clk PL0_REF>, <&zynqmp_clk PL1_REF>;
    clock-names = "s_axi_aclk", "intf_clk";
};

TDD core with external clock source (e.g. HMC7044):

axi_tdd_0: axi-tdd-0@9c460000 {
    compatible = "adi,axi-tdd";
    reg = <0x9c460000 0x10000>;
    clocks = <&zynqmp_clk PL0_REF>, <&hmc7044 6>;
    clock-names = "s_axi_aclk", "intf_clk";
};

Driver testing

The TDD driver exposes a sysfs interface for configuration and monitoring.

Sysfs interface

The driver creates sysfs attributes under /sys/bus/platform/drivers/adi-axi-tdd/<device>/.

Core attributes

Attribute

Access

Description

version

RO

IP core version (semantic versioning)

core_id

RO

ID configuration parameter value

magic

RO

Identification code (0x5444444E = ‘T’,’D’,’D’,’N’)

scratch

RW

Scratch register for testing register access

state

RO

Current state of the peripheral FSM

Control attributes

Attribute

Access

Description

enable

RW

Enable or disable the TDD module

sync_soft

WO

Trigger one software sync pulse

sync_external

RW

Enable or disable the external sync trigger

sync_internal

RW

Enable or disable the internal sync trigger

sync_reset

RW

Reset the internal counter on sync event

Timing attributes

Attribute

Access

Description

burst_count

RW

Number of frames per burst (0 = continuous)

startup_delay_raw

RW

Initial delay before first frame (clock cycles)

startup_delay_ms

RW

Initial delay before first frame (milliseconds)

frame_length_raw

RW

Frame length (clock cycles)

frame_length_ms

RW

Frame length (milliseconds)

internal_sync_period_raw

RW

Internal sync generator period (clock cycles)

internal_sync_period_ms

RW

Internal sync generator period (milliseconds)

Per-channel attributes (N = 0..31)

Attribute

Access

Description

out_channelN_enable

RW

Enable or disable output channel N

out_channelN_polarity

RW

Set the polarity of output channel N

out_channelN_on_raw

RW

Channel N assertion offset from frame start (clock cycles)

out_channelN_off_raw

RW

Channel N de-assertion offset from frame start (clock cycles)

out_channelN_on_ms

RW

Channel N assertion offset from frame start (milliseconds)

out_channelN_off_ms

RW

Channel N de-assertion offset from frame start (milliseconds)

Example sysfs usage

The following example configures a burst of 2 pulses on channel 0, triggered by an external sync signal (e.g. a GPSDO 1 PPS output). Each frame is 2 ms long, and within each frame channel 0 is high from 0.8 ms to 1.8 ms (a 1 ms pulse). After the 2 frames complete, the output stays low until the next external sync event re-arms the core.

~$
cd /sys/bus/platform/drivers/adi-axi-tdd/9c460000.axi-tdd-0

# Configure timing (must be done before enabling)
/sys/bus/platform/drivers/adi-axi-tdd/9c460000.axi-tdd-0$
echo 0.000000 > startup_delay_ms
/sys/bus/platform/drivers/adi-axi-tdd/9c460000.axi-tdd-0$
echo 2.000000 > frame_length_ms
/sys/bus/platform/drivers/adi-axi-tdd/9c460000.axi-tdd-0$
echo 2 > burst_count

# Configure channel 0: 1 ms pulse starting at 0.8 ms into each frame
/sys/bus/platform/drivers/adi-axi-tdd/9c460000.axi-tdd-0$
echo 0.800000 > out_channel0_on_ms
/sys/bus/platform/drivers/adi-axi-tdd/9c460000.axi-tdd-0$
echo 1.800000 > out_channel0_off_ms
/sys/bus/platform/drivers/adi-axi-tdd/9c460000.axi-tdd-0$
echo 1 > out_channel0_enable

# Use external sync as trigger source and enable
/sys/bus/platform/drivers/adi-axi-tdd/9c460000.axi-tdd-0$
echo 1 > sync_external
/sys/bus/platform/drivers/adi-axi-tdd/9c460000.axi-tdd-0$
echo 1 > enable

# Verify the core is armed and waiting for sync
/sys/bus/platform/drivers/adi-axi-tdd/9c460000.axi-tdd-0$
cat state

Explanation of the configuration:

  • startup_delay_ms = 0: No extra delay before the first frame. Note that any startup delay applies to all channels equally.

  • frame_length_ms = 2: Each frame is 2 ms long; GPIO toggles are spaced 2 ms apart.

  • burst_count = 2: A burst of 2 frames is generated, then the core stops. Set to 0 for infinite (continuous) operation.

  • out_channel0_on_ms = 0.8: Channel 0 asserts 0.8 ms after the frame starts. This acts as a per-channel delay relative to the sync trigger.

  • out_channel0_off_ms = 1.8: Channel 0 de-asserts 1.8 ms after the frame starts. The resulting pulse width is 1.0 ms (1.8 - 0.8).

  • sync_external = 1: The core waits for an external sync pulse to begin the burst.

  • enable = 1: Arms the TDD core; it enters the ARMED state and waits for the configured sync event.

Theory of operation

Frame generation

The central idea of the TDD controller is “frame”-based operation: all timing defined for individual channels is relative to the beginning of a frame. The frame_length value controls the length of a single frame, while burst_count controls how many frames should be played after enabling the device (a value of 0 means frames repeat indefinitely). Before the start of a burst, an optional startup delay is inserted, defined by startup_delay in clock cycles.

The TDD core generates timing waveforms organized into frames:

  1. When enabled, the core enters the ARMED state and waits for a sync event

  2. After sync, it transitions to the WAITING state and the startup delay counter runs (if configured)

  3. The core enters the RUNNING state and the frame counter begins, cycling through the configured frame length

  4. Each channel output is asserted at its on time and de-asserted at its off time within the frame

  5. After the frame completes, the next frame begins

  6. If burst_count is non-zero, the core stops after that many frames and returns to IDLE; otherwise it runs continuously until disabled

FSM states

The peripheral transitions between four states. The current state can be read via the state sysfs attribute, which returns the numeric value:

Value

State

Description

0

IDLE

The core is disabled or has completed its burst

1

ARMED

The core is enabled and waiting for a synchronization event

2

WAITING

A sync has been received; the startup delay is counting down

3

RUNNING

Frames are actively being generated

Synchronization

Three sync sources are available and can be enabled simultaneously:

  • Software (sync_soft): Write-only, generates a single pulse. Useful for triggering operation at an arbitrary point in time.

  • Internal (sync_internal): Periodic trigger from an internal counter. The period is configured via internal_sync_period_raw/_ms.

  • External (sync_external): An external signal input, optionally processed through a CDC circuit (synthesis-time configuration). This allows alignment of frames between multiple devices in different locations, for example using a GPSDO 1 PPS output.

If a synchronization signal is received while the TDD core is already running, the sync_reset attribute controls whether the internal counter is reset to zero. This can alter the counter value in both the WAITING and RUNNING states.

Channel output

The TDD controller can have up to 32 output channels. Each channel produces a single-bit output signal with unique on and off values that are continuously compared to the internal counter while the core is RUNNING.

Within a frame:

  • The output is asserted (set to active level) at the on time

  • The output is de-asserted (set to inactive level) at the off time

  • The polarity attribute inverts the active level for a channel

Every bit in CHANNEL_ENABLE / CHANNEL_POLARITY corresponds to a specific channel. The bit position is correlated to the channel index: the LSB is associated with channel 0 and the MSB with channel 31.

Timing values can be specified in raw clock cycles or milliseconds. The millisecond values are converted to clock cycles using the intf_clk rate, which is tracked dynamically via clock notifiers.

Register update restrictions

Warning

Most configuration registers cannot be modified while the TDD core is enabled. Any writes to these registers while the peripheral is enabled will be ignored.

The following attributes must be configured before enabling the peripheral:

  • burst_count

  • startup_delay_raw / startup_delay_ms

  • frame_length_raw / frame_length_ms

  • internal_sync_period_raw / internal_sync_period_ms

  • out_channelN_polarity

  • out_channelN_on_raw / out_channelN_on_ms

  • out_channelN_off_raw / out_channelN_off_ms

Exceptions:

  • out_channelN_enable can be modified on-the-fly. The new value takes effect when the core enters the ARMED state or at the end of the current frame.

  • enable, sync_soft, sync_external, sync_internal, and sync_reset (the CONTROL register) can be modified on-the-fly with immediate effect.

References