ADAR1000

ADAR1000 8 GHz to 16 GHz, 4-Channel, X Band and KuBand Beamformer Linux device driver.

The ADAR1000 is a 4-channel, X and Ku frequency band, beamforming core chip for phased arrays. This device operates in half-duplex between receive and transmit modes. In receive mode, input signals pass through four receive channels and are combined in a common RF_IO pin. In transmit mode, the RF_IO input signal is split and passes through the four transmit channels. In both modes, the ADAR1000 provides a ≥31 dB gain adjustment range and a full 360° phase adjustment range in the radio frequency (RF) path, with 6-bit resolution (less than ≤0.5 dB and 2.8°, respectively).

Supported Devices

Evaluation Boards

Description

This is a Linux industrial I/O (Linux Industrial I/O Subsystem) subsystem driver, targeting RF Transceivers. The industrial I/O subsystem provides a unified framework for drivers for many different types of converters and sensors using a number of different physical interfaces (i2c, spi, etc). See Linux Industrial I/O Subsystem for more information.

Source Code

Status

Source

Mainlined?

git

No

Files

Example Linux Device-Tree Initialization

The ADAR1000 driver is a spi-bus driver and can currently only be instantiated via device tree.

Required devicetree properties:

  • compatible: Should always be adi,adar1000

  • reg: SPI slave select number

Function

File

RPI Device Tree

rpi-adar1000-overlay.dts

Enabling Linux driver support

Configure kernel with make menuconfig (alternatively use make xconfig or make qconfig)

Note

The ADAR1000 driver depends on CONFIG_SPI

Adding Linux driver support

Configure kernel with make menuconfig (alternatively use make xconfig or make qconfig)

Linux Kernel Configuration
    Device Drivers  --->
    <*>     Industrial I/O support --->
        --- Industrial I/O support
        -*-   Enable ring buffer support within IIO
        -*-     Industrial I/O lock free software ring
        -*-   Enable triggered sampling support

              *** Analog to digital converters ***
        [--snip--]

        <*>   Analog Devices ADAR1000 Beamformer driver

        [--snip--]

Driver testing / API

Important

SPI writes and reads for certain attributes are delayed because of how the hardware behaves. So, updates to certain properties take a couple of seconds to be reflected in a read-back from hardware.

Each and every IIO device, typically a hardware chip, has a device folder under /sys/bus/iio/devices/iio:deviceX. Where X is the IIO index of the device. Under every of these directory folders reside a set of files, depending on the characteristics and features of the hardware device in question. These files are consistently generalized and documented in the IIO ABI documentation. In order to determine which IIO deviceX corresponds to which hardware device, the user can read the name file /sys/bus/iio/devices/iio:deviceX/name. In case the sequence in which the iio device drivers are loaded/registered is constant, the numbering is constant and may be known in advance.

General attribute naming convention: in_voltage0_[…]: targets RX1 in_voltage1_[…]: targets RX2

out_voltage0_[…]: targets TX1 out_voltage1_[…]: targets TX2

root:/> cd /sys/bus/iio/devices/
root:/sys/bus/iio/devices> ls
iio:device0  iio:device1  iio:device2  iio:device3

root:/sys/bus/iio/devices> cd iio:device0

root:/sys/bus/iio/devices/iio:device1# ls -l
total 0
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 bias_current_rx
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 bias_current_rx_lna
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 bias_current_tx
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 bias_current_tx_drv
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 dev
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 gen_clk_cycles
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_temp0_raw
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage0_RX_beam_pos_load
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage0_RX_beam_pos_save
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage0_RX_hardwaregain
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage0_RX_phase
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage0_RX_powerdown
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage1_RX_beam_pos_load
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage1_RX_beam_pos_save
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage1_RX_hardwaregain
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage1_RX_phase
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage1_RX_powerdown
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage2_RX_beam_pos_load
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage2_RX_beam_pos_save
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage2_RX_hardwaregain
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage2_RX_phase
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage2_RX_powerdown
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage3_RX_beam_pos_load
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage3_RX_beam_pos_save
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage3_RX_hardwaregain
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage3_RX_phase
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage3_RX_powerdown
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage_bias_set_load
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage_bias_set_save
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage_sequence_end
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 in_voltage_sequence_start
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 label
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 lna_bias_off
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 lna_bias_on
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 name
lrwxrwxrwx 1 root root    0 Oct 27 09:10 of_node -> ../../../../../../../../firmware/devicetree/base/fpga-axi@0/axi_quad_spi@85200000/adar1000@1/dev@0
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_beam_pos_load
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_beam_pos_save
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_detector_en
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_hardwaregain
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_pa_bias_off
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_pa_bias_on
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_phase
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_powerdown
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage0_TX_raw
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_beam_pos_load
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_beam_pos_save
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_detector_en
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_hardwaregain
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_pa_bias_off
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_pa_bias_on
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_phase
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_powerdown
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage1_TX_raw
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_beam_pos_load
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_beam_pos_save
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_detector_en
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_hardwaregain
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_pa_bias_off
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_pa_bias_on
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_phase
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_powerdown
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage2_TX_raw
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_beam_pos_load
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_beam_pos_save
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_detector_en
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_hardwaregain
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_pa_bias_off
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_pa_bias_on
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_phase
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_powerdown
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage3_TX_raw
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage_bias_set_load
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage_bias_set_save
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage_sequence_end
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 out_voltage_sequence_start
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 phase_table_config
drwxrwxrwx 2 root root    0 Oct 27 09:10 power
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 reset
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 rx_lna_enable
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 rx_vga_enable
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 rx_vm_enable
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 sequencer_enable
lrwxrwxrwx 1 root root    0 Oct 27 09:10 subsystem -> ../../../../../../../../bus/iio
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 tx_lna_enable
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 tx_vga_enable
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 tx_vm_enable
-rw-rw-rw- 1 root root 4096 Oct 27 09:10 uevent
root:/sys/bus/iio/devices/iio:device0#

Show device name

root:/sys/bus/iio/devices/iio:device0> cat name
adar1000

RX/TX Phase Control

The default phase control is provided by table 13 of the ADAR1000 datasheet. This is the configuration used at device startup.

Standard Table:

Phase (Degrees)

I Reg (Hex)

Q Reg (Hex)

0 … 360

0x3F

0x20

Custom phase tables can be loaded automatically during driver probe or anytime later via the phase_table_config sysfs attribute. Tables must be stored in the /firmware folder, or compiled into the kernel using the CONFIG_FIRMWARE_IN_KERNEL, CONFIG_EXTRA_FIRMWARE config options. The table loaded during driver probe can be specified using following device tree property:

adi,phasetable-name = adar1000_std_phasetable;

In case no table is specified or loaded, the driver will continue to use the provided standard phase table.

Phase table is stored in a human readable file, with the format specified below.

Example: adar1000_std_phasetable

<phasetable [ADAR1000] table entries=no_of_entries>
phase_in_degrees, reg_I, reg_Q
phase_in_degrees, reg_I, reg_Q
phase_in_degrees, reg_I, reg_Q
…
</phasetable>

Assumptions:

  • phase tables must be monotonic

Loading a gain table

root@analog:/sys/bus/iio/devices/iio:device0# ls /firmware/adar
adar1000_std_phasetable

root@analog:/sys/bus/iio/devices/iio:device0# cat /firmware/adar1000_std_phasetable > phase_table_config

Reading a gain table

Reading the phase_table_config attribute returns the current phase table that is loaded into the IC. The printed format matches the format used in the phasetable file.

root@analog:/sys/bus/iio/devices/iio:device0# cat phase_table_config
<phasetable ADAR1000 table entries=64>
0.000, 0x3F, 0x20
2.812, 0x3F, 0x21
5.625, 0x3F, 0x23
8.437, 0x3F, 0x24
11.250, 0x3F, 0x26
14.040, 0x3E, 0x27
16.875, 0x3E, 0x28
19.687, 0x3D, 0x2A
22.500, 0x3D, 0x2B
25.312, 0x3C, 0x2D

[--snip--]

165.937, 0x1E, 0x27
168.750, 0x1E, 0x26
171.562, 0x1F, 0x24
174.375, 0x1F, 0x23
177.187, 0x1F, 0x21
</phasetable>

RAM memory access

On-chip RAM is provided for storing phase and amplitude settings for up to 121 beam positions and seven bias settings for both transmit and receive modes.

Save/Load beam position

A beam position can be written to ram and loaded afterwards from that memory location (index).

Save beam position command
echo 0, -11.0, 15.4 > /sys/bus/iio/devices/iio\:device0/in_voltage1_RX_beam_pos_save

This command can be decoded using this enumeration:

Tip

beam_position, gain_value, phase_value

Values for gain_value and phase_value must be in integer and fractional part. beam_position can take values from 0 to 120.

Load beam position command
echo 10 > in_voltage1_RX_beam_pos_load

To load a position just write the position number in the load attribute for the required channel.

Save/Load bias settings

Seven memory locations are also provided for storing bias settings for all the transmit and receive channel subcircuits.

Save RX bias settings
echo 0, 1, 2, 3, 4 > in_voltage_bias_set_save

The values written here correspond the the following diagram:

Tip

bias_setting_index, LNA_BIAS_OFF, LNA_BIAS_ON, BIAS_CURRENT_RX, BIAS_CURRENT_RX_LNA

Save TX bias settings
echo 1, 10, 2, 3, 5, 6, 7, 8, 9, 10, 11 > out_voltage_bias_set_save

The values written here correspond the the following diagram:

Tip

bias_setting_index, CH1_PA_BIAS_OFF, CH2_PA_BIAS_OFF, CH3_PA_BIAS_OFF, CH1_PA_BIAS_ON, CH2_PA_BIAS_ON, CH3_PA_BIAS_ON, CH4_PA_BIAS_OFF, CH4_PA_BIAS_ON, BIAS_CURRENT_TX, BIAS_CURRENT_TX_DRV

Load bias settings
echo 5 > out_voltage_bias_set_load
echo  > in_voltage_bias_set_load

For loading a different bias setting the operations is the same for RX and TX channels.

Memory sequencer

The beam can be stepped sequentially through the positions stored in memory. To use this feature the following steps must be executed:

  1. load sequence start

  2. load sequence end

  3. enable sequencer

  4. generate clock cycles

  5. pulse TX/RX Load pin

echo 10 > in_voltage_sequence_start
echo 20 > in_voltage_sequence_end
echo 1 > sequencer_enable
echo 1 > gen_clk_cycles

Low level register access via debugfs (direct_reg_access)

Some IIO drivers feature an optional debug facility, allowing users to read or write registers directly. Special care needs to be taken when using this feature, since you can modify registers on the back of the driver.

Tip

To simplify direct register access you may want to use the libiio iio_reg command line utility.

Accessing debugfs requires root privileges.

In order to identify if the IIO device in question feature this option you first need to identify the IIO device number.

Therefore read the name attribute of each IIO device:

~$
grep "" /sys/bus/iio/devices/iio\:device*/name
 /sys/bus/iio/devices/iio:device0/name:ad7291
 /sys/bus/iio/devices/iio:device1/name:ad9361-phy
 /sys/bus/iio/devices/iio:device2/name:xadc
 /sys/bus/iio/devices/iio:device3/name:adf4351-udc-rx-pmod
 /sys/bus/iio/devices/iio:device4/name:adf4351-udc-tx-pmod
 /sys/bus/iio/devices/iio:device5/name:cf-ad9361-dds-core-lpc
 /sys/bus/iio/devices/iio:device6/name:cf-ad9361-lpc

Change directory to /sys/kernel/debug/iio/iio:deviceX and check if the direct_reg_access file exists.

~$
cd /sys/kernel/debug/iio/iio\:device1
/sys/kernel/debug/iio/iio:device1$
ls direct_reg_access
direct_reg_access

Reading

/sys/kernel/debug/iio/iio:device1$
echo 0x7 > direct_reg_access
/sys/kernel/debug/iio/iio:device1$
cat direct_reg_access
 0x40

Writing

Write ADDRESS VALUE:

/sys/kernel/debug/iio/iio:device1$
echo 0x7 0x50 > direct_reg_access
/sys/kernel/debug/iio/iio:device1$
cat direct_reg_access
 0x50

Accessing HDL CORE registers

Special ADI device driver convention for devices that have both:

  • a SPI/I2C control interface

  • and some sort of HDL Core with registers (AXI)

In this case when accessing the HDL Core Registers always set BIT31.

The register map for the ADI HDL IP cores are documented at each IP page at IP Cores, section “Register Map”.

/sys/kernel/debug/iio/iio:device6$
echo 0x80000000 > direct_reg_access
/sys/kernel/debug/iio/iio:device6$
cat direct_reg_access
 0x80062

More Information