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
Files
Function |
File |
|
|---|---|---|
driver |
||
devicetree bindings |
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,adar1000reg: SPI slave select number
Function |
File |
|---|---|
RPI Device Tree |
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:
load sequence start
load sequence end
enable sequencer
generate clock cycles
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