diff options
Diffstat (limited to 'host/docs/zbx.dox')
-rw-r--r-- | host/docs/zbx.dox | 469 |
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: |