aboutsummaryrefslogtreecommitdiffstats
path: root/host/docs/zbx.dox
diff options
context:
space:
mode:
Diffstat (limited to 'host/docs/zbx.dox')
-rw-r--r--host/docs/zbx.dox469
1 files changed, 469 insertions, 0 deletions
diff --git a/host/docs/zbx.dox b/host/docs/zbx.dox
new file mode 100644
index 000000000..76c5585f6
--- /dev/null
+++ b/host/docs/zbx.dox
@@ -0,0 +1,469 @@
+/*! \page page_zbx ZBX Daughterboard
+
+\tableofcontents
+
+\section zbx_overview Overview
+
+The ZBX daughterboard is a two-channel superheterodyne transceiver with a focus
+of telecommunication applications in the frequency range below 8 GHz. It supports
+analog bandwidths of up to 400 MHz.
+
+The ZBX daughterboard is designed for use with the Ettus USPR X410.
+
+Feature list:
+- Frequency range (Tx and Rx): 1 MHz - 7.2 GHz (Note: Tune range extends to 8 GHz)
+- Maximum analog bandwidth: 400 MHz
+- Gain range: 0-60 dB.
+ - Note: Rx gain range is reduced to 0-38 dB for frequencies below 500 MHz.
+- On-board CPLD for high flexibility
+- Maximum output power: 5-20 dBm (depending on frequency)
+- Maximum input power limit: +15 dBm
+
+\section zbx_too Theory of Operations
+
+The ZBX daughterboard has two transceiver chains. The following simplified block
+diagram shows their structure:
+
+\image html ZBX_simplified_blockdiagram.svg "ZBX Block Diagram"
+
+It is a superheterodyne transceiver with up to two IF stages. The second IF
+stage is only used for center frequencies below 3 GHz. Above that frequency, the
+desired center frequency becomes the first intermediate frequency (IF1). The
+second LO stage is always enabled, and moves the IF to a value between 1 and 2
+GHz. The USRP ADC/DAC (running at a sampling rate of approx. 3 GHz) will sample
+the IF directly, and downconvert to or from DC digitally.
+
+The TX and RX paths are almost symmetric, with slight variations on the various
+frequency bands. The various gain stages are spread out along the TX and RX
+paths (see also \ref zbx_gain_control, note that the TX path includes selectable
+amplifiers as well as DSAs). All LO synthesizers are identical (LMX2572).
+
+\subsection zbx_too_cpld Digital Control
+
+For digital controls, the ZBX includes a CPLD (its source code is part of the
+UHD repository, and can be found under `fpga/usrp3/top/x400/dboards/zr/cpld/`).
+The CPLD is controlled via registers. Its register space is exposed as a subset
+of the Radio RFNoC block register space (starting at address 0x80000). The CPLD
+is used to control all switches, DSAs, amplifiers, LEDs, LO synthesizers and
+power rails. The CPLD also controls state-dependent behaviour of the ZBX (i.e.,
+behaviour depending on the RX/TX state). For this purpose, ATR signals from the
+FPGA are routed to the CPLD. Parts of the CPLD feature set are also described in
+\ref zbx_gain_control.
+
+
+\subsection zbx_too_lo_control LO Control
+
+The normal operation of the ZBX daughterboard is to simply tune it to a desired
+center frequency, and UHD will internally calculate frequencies for the
+individual LOs as well as the NCO. UHD uses a few rules when calculating LO and
+NCO frequencies:
+
+- To simplify the algorithms, LO frequencies are quantized to multiples
+ of the LO reference frequency (which itself depends on the master clock rate).
+- To reduce LO spurs, the LO synthesizer outputs are filtered with an analog
+ bandpass filter with a minimum frequency of 3.2 GHz.
+- To avoid <em>injection locking</em> (an effect where nearby synthesizers
+ influence each other), UHD is programmed to choose different frequencies for
+ the various synthesizers. When programming the two channels to the same
+ frequency, the LOs will thus intentionally run at different frequencies. The
+ combination of the various LOs and the NCO frequency still results in the same
+ center frequency.
+
+The state of individual LOs can be queried and configured independently. Use the
+following API calls to do so:
+- multi_usrp API:
+ - uhd::usrp::multi_usrp::get_rx_lo_freq()
+ - uhd::usrp::multi_usrp::get_rx_lo_freq_range()
+ - uhd::usrp::multi_usrp::set_rx_lo_freq()
+ - uhd::usrp::multi_usrp::get_tx_lo_freq()
+ - uhd::usrp::multi_usrp::get_tx_lo_freq_range()
+ - uhd::usrp::multi_usrp::set_tx_lo_freq()
+- RFNoC API:
+ - uhd::rfnoc::radio_control::get_rx_lo_freq()
+ - uhd::rfnoc::radio_control::get_rx_lo_freq_range()
+ - uhd::rfnoc::radio_control::set_rx_lo_freq()
+ - uhd::rfnoc::radio_control::get_tx_lo_freq()
+ - uhd::rfnoc::radio_control::get_tx_lo_freq_range()
+ - uhd::rfnoc::radio_control::set_tx_lo_freq()
+
+Note that manually modifying LOs is considered advanced behaviour, and may result
+in a bad state of the device. To undo manual changes, use the regular API calls
+to set a center frequency.
+
+\section zbx_ant_ports Antenna Ports
+
+The ZBX has two SMA ports per channel, called "TX/RX0" and "RX1".
+In addition, the antenna values can be set to "CAL_LOOPBACK"
+to loop back the Tx path into the Rx path (this is sometimes required for
+calibration purposes). The Rx antenna value can also be set to "TERMINATION" to
+terminate the Rx path.
+
+Use the uhd::usrp::multi_usrp::get_rx_antennas() or uhd::usrp::multi_usrp::get_tx_antennas()
+API calls to enumerate the valid antenna names. When using RFNoC API, use the
+uhd::rfnoc::radio_control::get_rx_antennas() and
+uhd::rfnoc::radio_control::get_tx_antennas() calls, respectively.
+
+\section zbx_phase_alignment Phase Alignment
+
+Like the UBX and SBX daughterboards, the ZBX allows to be phase-aligned. This is
+done by setting a command time before tuning the individual channels:
+~~~{.cpp}
+// Assume that `usrp` is a multi_usrp object
+// 1) Set a command time in the future, e.g. 500ms from now:
+usrp->set_command_time(usrp->get_time_now() + .5);
+// 2) Tune to a new frequency on all channels, e.g., 2 GHz:
+usrp->set_rx_freq(2e9); // The ALL_CHANS argument is implied here
+// 3) Wait until we're past the command time:
+std::this_thread::sleep_for(500ms);
+// Channel phases are now at a deterministic offset. Repeating this procedure
+// will lead to the same offset.
+~~~
+
+\section zbx_pwr_cal Power Calibration
+
+The ZBX supports the UHD power API (see also \ref page_power). UHD ships with
+nominal calibration data which will allow setting the reference power levels
+without previously manually calibrating the device.
+
+\section zbx_sensors Sensors
+
+Every channel has three "locked" sensors for the LO stages (`lo1_locked`,
+`lo2_locked`, and `nco_locked`). A "virtual" sensor called `lo_locked` confirms
+that all LOs that are currently engaged are locked. The "NCO lock" sensor is a
+special case: The NCO is not on the daughterboard (it is part of the
+RFSoC FPGA), but to simplify the API it was placed together with the LO lock
+sensors. Unlike the (analog) synthesizers on the daughterboard, the NCO "unlock"
+is not used to signify a loss of reference lock, but to signal that the NCO is
+still in reset.
+
+Additionally, the ZBX has a temperature sensors `temperature`. While the UHD
+API allows addressing a sensors based on direction (RX/TX) and channel (0/1),
+there is only one physical temperature sensor, and it will return the same value
+regardless of which channel or direction is selected.
+
+The following API calls can be used to enumerate available sensors, and query
+their values:
+- multi_usrp API:
+ - uhd::usrp::multi_usrp::get_rx_sensor_names()
+ - uhd::usrp::multi_usrp::get_rx_sensor()
+ - uhd::usrp::multi_usrp::get_tx_sensor_names()
+ - uhd::usrp::multi_usrp::get_tx_sensor()
+- RFNoC API:
+ - uhd::rfnoc::radio_control::get_rx_sensor_names()
+ - uhd::rfnoc::radio_control::get_rx_sensor()
+ - uhd::rfnoc::radio_control::get_tx_sensor_names()
+ - uhd::rfnoc::radio_control::get_tx_sensor()
+
+\section zbx_gain_control Gain Control
+
+The ZBX has a sophisticated gain control, capable of controlling either an
+overall gain, or manually controlling its individual gain stages. Furthermore,
+the onboard CPLD can store gain tables as well, which can be accessed from other
+RFNoC blocks via RFNoC commands.
+
+The TX path has three gain-related components: Two DSAs, as well an amplifier path.
+The former have an individual gain range of 31 dB. The amplifier path allows
+selecting one of two amplifiers, one with a nominal gain of 14 dB for lower
+frequencies, and one with a nominal gain of 21 dB for higher frequencies. Their
+actual amplification values depend on the specific frequency, and may also vary
+from device to device. The amplifiers can be bypassed.
+
+The RX path consists of four DSAs, each with a gain range of 15 dB.
+
+Different gain behaviours of the ZBX daughterboard are controlled by <em>gain
+profiles</em>, which may be set independently for TX and RX. Different gain
+profiles have different API behaviours as explained in the rest of this section.
+
+To switch between gain profiles, use the uhd::usrp::multi_usrp::set_tx_gain_profile()
+or uhd::usrp::multi_usrp::set_rx_gain_profile() API calls. When using the RFNoC
+API, use the uhd::rfnoc::radio_control::set_tx_gain_profile() or
+uhd::rfnoc::radio_control::set_rx_gain_profile() API calls. The main difference
+between these APIs is that the multi_usrp API calls allow a default channel
+value, which the RFNoC API calls do not.
+
+As with all other devices, the following API calls set or query gain values:
+- Multi USRP:
+ - uhd::usrp::multi_usrp::set_tx_gain()
+ - uhd::usrp::multi_usrp::get_tx_gain()
+ - uhd::usrp::multi_usrp::set_rx_gain()
+ - uhd::usrp::multi_usrp::get_rx_gain()
+- RFNoC
+ - uhd::rfnoc::radio_control::set_tx_gain()
+ - uhd::rfnoc::radio_control::get_tx_gain()
+ - uhd::rfnoc::radio_control::set_rx_gain()
+ - uhd::rfnoc::radio_control::get_rx_gain()
+
+These API calls come in different flavours, with an optional 'gain name' argument.
+Note the multi_usrp API calls default to setting the overall gain value, which
+is not allowed in all gain profiles. All API calls have a corresponding API call
+to query the allowable gain range.
+
+\b Note: The RX gain range is not consistent on ZBX. Below 500 MHz, the gain
+range is reduced to 0-38 dB. It is recommended to use get_rx_gain_range() to
+query the currently valid gain range.
+
+\b Note: Some gain profiles require changing on both TX and RX. For example,
+changing from `default` to `table_noatr` must happen on TX and RX at the same
+time. UHD will automatically change the gain profile accordingly. It is therefore
+recommended to call get_rx_gain_profile() or get_tx_gain_profile() to verify the
+correct gain profile when in doubt.
+
+\subsection zbx_gain_default Default Gain Profile
+
+This gain profile is active by default. It allows setting a single, scalar gain
+value. UHD will internally use a gain table to linearize the overall gain (meaning
+that a 1 dB gain increase will also increase the transmit or receive power by 1 dB).
+However, the UHD-internal gain table is not calibrated per-device, nor does it
+take into account temperature or other changes.
+
+In this gain mode, it is not possible to set the individual gain stages directly,
+but it is possible to read them back. This may be helpful when trying to fine-tune
+gain settings in software.
+
+~~~{.cpp}
+// Assumption: 'usrp' is a multi_usrp object
+usrp->set_tx_gain_profile("default"); // Only necessary if the gain profile was set to something else before
+usrp->set_tx_gain(30); // Will set the gain to 30 dB on all associated daughterboards
+std::cout << usrp->get_tx_gain() << std::endl; // Should print "30"
+usrp->set_tx_gain(0, "DSA1"); // Will cause an exception
+// Individual DSAs may still be queried. Note that even though this is an
+// attenuator, the return value is a gain (higher values mean more TX power):
+auto dsa1_gain = usrp->get_tx_gain("DSA1");
+~~~
+
+<b>ATR Behaviour:</b> The gains will apply to their respective ATR state, i.e.,
+RX gains will be applied to both the RX and full-duplex state, and TX gains will
+be applied to the TX and full-duplex state. In the idle state, gains are set to
+minimum gain.
+
+<b>RFNoC Commands:</b> All DSA values are on one register for a given ATR state,
+and the TX amplifier shares a register with the antenna controls.
+That means that changing the DSA values
+in this gain profile will cause two register writes (one for RX/TX, and one for
+full-duplex). Changing the TX amplifier gain value will incur another two writes.
+
+\subsection zbx_gain_manual Manual Gain Profile
+
+When more control is desired, the manual gain profile can be applied. Here, it
+is no longer possible to request an overall gain value. However, it is now
+possible to set the DSA and amplifier values directly.
+
+~~~{.cpp}
+usrp->set_tx_gain_profile("manual");
+usrp->set_tx_gain(30); // ERROR: Now, we have to specify a name
+usrp->set_tx_gain(5, "DSA1"); // Set DSA1 to 5 dB gain (equals 26 dB attenuation)
+// The following line will return an undefined value and print a warning, but
+// will not throw. That's because calling the overall gain is a common API call
+// done by many utilities, and this behaviour is considered most backward compatible.
+std::cout << usrp->get_tx_gain() << std::endl;
+std::cout << usrp->get_tx_gain("DSA1") << std::endl; // Should print '5'
+~~~
+
+\subsection zbx_gain_table CPLD-Table Gain Profile with ATR control
+
+In this profile, UHD exposes access to the gain table stored on the CPLD. By default,
+the CPLD is initialized with the same gain table as UHD uses internally, i.e.,
+there is no difference in behaviour when using this profile, unless the gain
+table is modified.
+
+Setting DSA values directly from UHD is not possible in this profile, nor is setting
+an overall gain value. However, it is now possible to load an entry from the
+CPLD gain table and apply it to the current DSA settings.
+In this profile, the ATR behaviour for the DSAs is the same as in the 'default'
+or 'manual' profiles. That means loading a DSA table entry requires two writes
+to the CPLD, one for TX/RX, and one for full-duplex.
+
+The gain "values" are no longer interpreted as dB values, but refer to DSA table
+indices.
+
+An important use case of this gain profile is when testing CPLD gain tables, but
+not changing other aspects of the software control. Often, this is an intermediate
+debugging step while developing applications that use RFNoC commands to control
+the gain.
+
+Another use case of this profile is when running RFNoC applications, where the
+gain is controlled from another RFNoC block, but the ATR behaviour is left in
+its default state.
+
+~~~{.cpp}
+usrp->set_tx_gain_profile("table");
+usrp->set_tx_gain(30); // ERROR: Now, we have to specify a name
+usrp->get_radio_control().set_tx_gain(5); // This works, though. The radio_control
+ // object is smart enough to infer that
+ // this is the only action left.
+// The previous line and the following have the same effect:
+usrp->set_tx_gain(5, "TABLE");
+// The CPLD DSA table entry at index 5 is now loaded and applied to the TX and
+// full-duplex ATR modes. In other words, the register values TX0_TABLE_DSA*
+// are copied to TX0_DSA*.
+// The following line will return an undefined value and print a warning, but
+// will not throw. That's because calling the overall gain is a common API call
+// done by many utilities, and this behaviour is considered most backward compatible.
+std::cout << usrp->get_tx_gain() << std::endl;
+// The following line will print the actual value of DSA1. Note, however, that
+// UHD needs to read back the value from the CPLD, since it can't know what's
+// stored on the CPLD. That means the following call will require a read from
+// the CPLD, which is not possible if there are timed commands queued up.
+std::cout << usrp->get_tx_gain("DSA1") << std::endl; // Should print whatever
+ // was originally stored
+ // in TX0_TABLE_DSA1, which
+ // was copied to TX0_DSA1
+~~~
+
+\subsection zbx_gain_tablenoatr CPLD-Table Gain Profile without ATR control
+
+When running applications where the FPGA has full control over the gain, and the
+ATR behaviour should also be replaced by register writes, this profile may be
+used.
+
+The main difference to the previous profile is that when the radio switches
+between RX, TX, full duplex, and idle states, there is no automatic update of
+the gain values. Instead, the current gain values are selected by writing to the
+`SW_RF0_DSA_CONFIG` and `SW_RF1_DSA_CONFIG` registers. Changing these registers
+will select an entry from the `RX/TX DSA` tables. Unlike the gain tables, these
+are not prepopulated.
+
+~~~{.cpp}
+usrp->set_tx_gain_profile("table_noatr");
+usrp->set_tx_gain(30); // ERROR: Now, we have to specify a name
+usrp->get_radio_control().set_tx_gain(5); // This works, though. The radio_control
+ // object is smart enough to infer that
+ // this is the only action left.
+// The previous line and the following have the same effect (but it's a different
+// effect than when using gain profile 'table'):
+usrp->set_tx_gain(5, "TABLE");
+// The CPLD SW_RF0_DSA_CONFIG register is now set to 5. That means the DSA values
+// stored in TX0_DSA1[5] and TX0_DSA2[5] are now being used, assuming no other
+// entity is sending commands to the CPLD via RFNoC.
+// Copying CPLD gain table entries into the TX0_DSA* registers is not possible
+// from software in this profile. Rather, we are hands-off and let the FPGA take
+// control.
+//
+// Let's assume that an RFNoC block has sent register writes to the radio block
+// in order to load new gain table entries, and apply them. We can still read
+// back the current DSA values (the ones being currently used on the RF chain)
+// by reading back from the CPLD.
+// This is not possible if there are timed commands queued up.
+std::cout << usrp->get_tx_gain("DSA1") << std::endl; // Should print whatever
+ // is currently in TX0_DSA1[i],
+ // where i is the value of
+ // SW_RF0_DSA_CONFIG.
+~~~
+
+\section zbx_atr Auto-Transmit-Receive Registers (ATR)
+
+Like other USRPs, the X410 provides GPIOs to the daughterboards that communicate
+the RX/TX state. The ZBX is, by default, configured to switch settings based on
+the current state (RX, TX, full duplex, idle). For example, the TX/RX antenna
+is switched between the TX and RX channels depending on the state. A total of 4
+GPIOs between the motherboard FPGA and the daughterboard CPLD are used for this
+purpose, two pins per channel.
+
+ZBX uses these pins to select between different RF control values
+(e.g., the aforementioned TX/RX antenna switch position; this also includes the
+front-panel LEDs) as well as DSA controls (e.g., when doing RX only, the TX gain
+stages can be set to zero to minimize leakage). Internally, this works by
+converting the ATR pins into a control word. This control word is used to index
+tables that contain different values for RF control/LEDs as well as DSAs.
+
+Example: Assume the device is transmitting, but not receiving, on channel 0. The
+FPGA will set the ATR pins for channel 0 to a binary value of 0b10, which equals
+a decimal value of 2. The transmit gain is controlled by DSA values that are
+stored in tables called TX0_DSA1 and TX0_DSA2, respectively. For the duration
+of the transmission, the DSA values at table position TX0_DSA1[2] and TX0_DSA2[2]
+will thus be used. Similarly, tables for RF path control and LEDs are used to
+configure those. This mode of using the ATR pins is called the "classic ATR" mode
+and is the default behaviour.
+
+The ZBX daughterboard provides two additional modes of utilizing those pins:
+- "Software Defined": In this mode, the ATR pins are ignored. The control word
+ is derived from another register. In this case, changing the table index
+ requires another register write as opposed to the almost instantaneous tracking
+ of the ATR state in the other modes.
+- "FPGA controlled": This is similar to the classic ATR mode, but it combines
+ the pins from channel 0 and 1. The downside is that channel 0 and 1 are no
+ longer independent, but it allows using 16 entries from the tables instead of
+ four as in the classic ATR mode.
+
+The ATR pin mode can be set independently for channel 0 and 1, and also for DSA
+tables vs. RF control/LED tables. Note that combining the "FPGA controlled" mode
+on one channel with the "classic" mode on the other channel would yield a possibly
+conflicting configuration.
+
+Usage of these modes is considered highly advanced usage of ZBX. The "FPGA
+controlled" mode is not supported by UHD without custom modifications (it is
+possible, however, to manually write to the appropriate registers to use this
+mode). Using this mode would also require modifications of the FPGA image to
+add custom controls to the ATR GPIO pins.
+
+The "software defined" mode can be enabled for the DSA tables by using the
+`table_noatr` gain profile (see the previous section).
+
+\section zbx_updating_cpld Updating the ZBX CPLD
+
+If you need to update the ZBX CPLD, you can do so by running the following command
+on the device:
+
+ python3 /usr/lib/python3.?/site-packages/usrp_mpm/dboard_manager/zbx_update_cpld.py
+
+By default, the script will attempt to install an image from the default path to
+both daughterboards. To specify which file or dboards to program, use the
+following options when running the updater script:
+
+- `--dboards=0,1`
+- `--file=<path_to_cpld_image>`
+
+By default, the `cpld-zbx.rpd` file will be provided at
+`/lib/firmware/ni/cpld-zbx.rpd`. Note that after downloading the ZBX CPLD, you
+will need to completely shut down and power-cycle the device.
+
+\subsection zbx_updating_cpld_details CPLD Programming: Details
+
+Read this section if you want to create your own ZBX CPLD image, or require more
+information on how the CPLD image is updated.
+
+The source code for the ZBX CPLD is provided within the UHD code repository, at
+`fpga/usrp3/top/x400/dboards/zbx/cpld`. Read the Makefile for more instructions
+on how to build images.
+
+The build process will produce two CPLD bitfiles: A `.rpd` file and a `.svf` file.
+Both are required for different programming methods. There are two programming
+methods:
+
+- Flash mode: This requires the `.rpd` file. It uses the motherboard CPLD as a
+ programming device. This is the default mode.
+- Legacy mode: This directly calls into openocd to flash the ZBX CPLD. It requires
+ the `.svf` file.
+
+Before running the updater, ensure that MPM is installed on your device as some
+resources from MPM code are used.
+
+You should also ensure that the power rails to the daughterboard are up and that
+MPM can successfully communicate with the daughterboard when running (running
+`uhd_usrp_probe` successfully is sufficient).
+
+MPM does not need to be running when the updater script is executed, but you
+should start it once to ensure a valid FPGA image is loaded and communication to
+the daughterboard is working before attempting this.
+
+To specify a programming mode, use the `--updater` argument. For example, to
+force the legacy mode, use the following command:
+
+ python3 /usr/lib/python3.?/site-packages/usrp_mpm/dboard_manager/zbx_update_cpld.py \
+ --updater=legacy \
+ --file=/lib/firmware/ni/cpld-zbx.svf
+
+This will program the image from the default location onto the CPLD using the
+'legacy' method.
+
+\section zbx_flash Flash Memory and EEPROM
+
+Every ZBX daughterboard has 2 MB of non-volatile flash memory which can be used
+to store data such as daughterboard-specific information. When
+logged into the X410 Linux system, the flash memory is mounted into the
+filesystem under `/mnt/db0_flash` and `/mnt/db1_flash`, respectively. The
+daughterboard also uses a separate EEPROM to store revision, serial, and product
+ID of the daughterboard.
+
+*/
+// vim:ft=doxygen: