|
|
Line 1: |
Line 1: |
− | ==Application Note Number==
| |
− | '''AN-TC'''
| |
| | | |
− | ==Revision History==
| |
− | {| class="wikitable"
| |
− | !Date
| |
− | !Author
| |
− | !Details
| |
− | |-
| |
− | |style="text-align:center;"| 2020-02-10
| |
− | |style="text-align:center;"| Sam Reiter
| |
− | |style="text-align:center;"| Initial creation
| |
− | |}
| |
− |
| |
− | ==Abstract==
| |
− | This AN discusses Timed Commands in UHD. We will explore use cases, theory of operation, and multi_usrp based examples of timed command used in UHD 3.x.
| |
− |
| |
− | ==Timed Commands: Overview and Usecases==
| |
− |
| |
− | Timed Commands are an important aspect of using a USRP. They allow a user to coordinate USRP state changes with nanosecond precision across multiple devices. Examples of timed command use cases include:
| |
− |
| |
− | <ul>
| |
− | <li> Configuring multiple channels of a single USRP to change frequency simultaneously.
| |
− | <li> Configuring the channels on multiple USRPs to synchronously retune and ensure a predictable phase offset between channels ([https://kb.ettus.com/Synchronization_and_MIMO_Capability_with_USRP_Devices#MIMO_Capability_of_Ettus_Research_USRP_Devices if supported by daughterboard]).
| |
− | <li> Changing the state of a USRP's GPIO line at an absolute time.
| |
− | <li> Coordinating a simultaneous change of gain and frequency in a USRP.
| |
− | <li> Scheduling frequency hopping at set time increments.
| |
− | <li> Using RFNoC blocks like the Replay Block to transmit phase coherent bursts.
| |
− | </ul>
| |
− |
| |
− |
| |
− | ===A System-Level Example===
| |
− | A common use case of timed commands is to ensure a predictable and repeatable phase offset between the channels of multiple USRPs. Before we dive into the low level details of timed commands, lets take a high level look at where they are used in configuring a phase coherent MIMO system.
| |
− |
| |
− | There are four key elements required for phase coherent operation of resync-capable USRPs:
| |
− | <ol>
| |
− | <li> All USRPs share a common reference clock (10MHz Ref)
| |
− | <li> All USRPs share a common sense of time (PPS)
| |
− | <li> LO and DSP tuning is synchronous
| |
− | <li> Streaming is started synchronously
| |
− | </ol>
| |
− | LO sharing is implemented in some USRP products, but will not be covered in this example. Here is a physical configuration satisfying the requirements listed above:
| |
− |
| |
− | [[File:Mimo Setup.png|1400px|center]]
| |
− |
| |
− |
| |
− | Now that the system has all the physical connections necessary, we need to coordinate things in our host code. This will include importing the 10MHz Ref and PPS as well as ensuring synchronous tuning and streaming.
| |
− |
| |
− | First, we need to create a <code>multi_usrp</code> object that contains both USRPs in our physical configuration:
| |
− |
| |
− | uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make("addr0=192.168.10.2,addr1=192.168.10.3");
| |
− |
| |
− | Next, we'll let the USRPs know that they should expect a 10MHz clock on their "Ref In" port, and a PPS signal on their "PPS In" port:
| |
− |
| |
− | usrp->set_clock_source("external");
| |
− | usrp->set_time_source("external");
| |
− |
| |
− | At this point, both USRPs are locked to an external reference and a common PPS signal. Next we want to tell both USRPs to reset their sense of time to 0.000s on the next PPS edge:
| |
− |
| |
− | usrp->set_time_next_pps(uhd::time_spec_t(0.0));
| |
− | std::this_thread::sleep_for(std::chrono::milliseconds(1000));
| |
− |
| |
− | We won't cover it much in this application note, but for now understand <code>time_spec_t</code> is the UHD API's means of formatting time values for a USRP. After we reset the USRP's sense of time, we wait 1 second to ensure a PPS rising edge occurs and latches the 0.000s value to both USRPs. At this point, both USRPs should have a shared sense of time. We've now satisfied the first and second requirements for phase coherent USRP operation. Let's move on to synchronously tuning using timed commands:
| |
− |
| |
− | usrp->clear_command_time();
| |
− |
| |
− | usrp->set_command_time(usrp->get_time_now() + uhd::time_spec_t(1.0));
| |
− |
| |
− | uhd::tune_request_t tune_request(freq);
| |
− | usrp->set_rx_freq(tune_request);
| |
− | std::this_thread::sleep_for(std::chrono::milliseconds(1)); //sleep 1ms to allow LO to lock
| |
− |
| |
− | usrp->clear_command_time();
| |
− |
| |
− | With the above code block, we are able to set a command time equal to the current time + 1s. Any commands that are called after <code>set_command_time()</code> will be sent to the USRP with a timestamp corresponding to the argument passed to <code>set_command_time()</code>. Because of this timestamp, the USRP will wait until the command time passes to execute the tune request. This will ensure that the LO and DSP chain of our USRPs are retuned synchronously (on the same clock cycle), satisfying the third requirement for phase coherent operation.
| |
− |
| |
− | The final step in configuring our system is to set up synchronous streaming from both radios. For this example, we'll set up synchronous RX streaming using the following code:
| |
− |
| |
− | // create a receive streamer
| |
− | uhd::stream_args_t stream_args("fc32", wire); // complex floats
| |
− | stream_args.channels = "0,1,2,3";
| |
− | uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args);
| |
− |
| |
− | // setup streaming
| |
− | uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
| |
− | stream_cmd.stream_now = false;
| |
− | stream_cmd.time_spec = uhd::time_spec_t(usrp->get_time_now() + uhd::time_spec_t(1.0));
| |
− | rx_stream->issue_stream_cmd(stream_cmd);
| |
− |
| |
− | Our system will begin to stream data 1s after the time returned by <code>usrp->get_time_now()</code>. Data sent to the host will be phase coherent between the 4 RX channels in this system. This architecture allows a system with X310 + UBX daughterboards to maintain a known phase relationship between channels across runs of code and system power cycles.
| |
− |
| |
− | ==Clocking and Timekeeping in the USRP==
| |
− |
| |
− | In this section, we will cover several key topics relating to USRP synchronization and the use of timed commands in UHD. This is the low level detail section of this application note.
| |
− |
| |
− | ===PPS (Pulse Per Second)===
| |
− |
| |
− | PPS is a signal used by USRPs for time synchronization. With a shared PPS, the sense of time can be aligned across several USRPs, allowing for the synchronization of timed command execution on an arbitrary number of radio channels. Within the context of a USRP, a PPS signal is expected to have the following properties:
| |
− |
| |
− | *1Hz
| |
− | *TTL Signal Levels
| |
− | *25% duty cycle
| |
− |
| |
− | A USRP's PPS can be derived from a GPSDO automatically, from an externally supplied PPS signal, or via internal PPS synthesis (not supported in legacy USRPs).
| |
− |
| |
− | A PPS trigger is used to coordinate time alignment events across multiple devices. For example, the USRPs internal sense of time (the <code>vita_time</code> counter) can be synchronously set/reset across multiple USRPs via UHD API calls such as:
| |
− |
| |
− | set_time_next_pps
| |
− |
| |
− | set_time_unknown_pps
| |
− |
| |
− | Ettus Research recommends the [https://www.ettus.com/all-products/octoclock/ Octoclock] for distribution of PPS and 10MHz REF signals across multiple devices, and the [https://www.ettus.com/all-products/octoclock-g/ Octoclock-G] for this functionality as well as tight alignment with GPS time. For further information on PPS and other common reference signals, see the [https://files.ettus.com/manual/page_sync.html UHD Manual: Device Synchronization].
| |
− |
| |
− | ===CHDR Packet Types and Structure===
| |
− |
| |
− | CHDR (pronounced "Cheddar", like the cheese) or "Compressed Header" packets are the packet type used to pass data and commands between a host computer and USRP. CHDR is a derivative of the VITA 49 (VRT) protocol which is proprietary to USRP devices. See [https://files.ettus.com/manual/page_rtp.html UHD Manual: Radio Transport Protocols] for more information.
| |
− |
| |
− | There are 4 types of packets used in the USRP:
| |
− | *Data
| |
− | *Flow Control
| |
− | *Command
| |
− | *Command Response
| |
− |
| |
− | The type of CHDR packet is determined by the value of bits 63 and 62 in the CHDR header:
| |
− |
| |
− | [[File:packet breakdown.png|1400px|center]]
| |
− |
| |
− |
| |
− |
| |
− | All of these packet types have a single bit (61) used to denote whether an optional timestamp is included. If present, a timestamp is a 64-bit value representing an absolute time value. In this application note, we’re concerned with the use and functionality of command packets with a timestamp present, also known as “timed commands”.
| |
− |
| |
− | ===Command Queue===
| |
− | As commands are passed from host to USRP, they are added to a FIFO on the USRP's FPGA. This FIFO is called the command queue and all commands that are sent to the USRP must pass through a command queue. Each block in the FPGA that handles data also has its own command queue. The command queue FIFO is not to be confused with the data FIFOs used to buffer data between blocks (pictured in Figure **###**).
| |
− |
| |
− | Every command queue maintains a sense of time. The mechanism for acquiring this sense of time is different between the Radio Core and other RFNoC blocks and will be explored later in this application note. When commands enter the command queue, their timestamp is compared against the command queue's sense of time and the commands are executed when Queue Time >= Command Time. Commands without timestamps are executed immediately when they're at the front of the queue. Command queues in RFNoC do not support on-the-fly reordering, meaning a command at the front of the queue will block subsequent commands from executing even if their timestamp has passed.
| |
− |
| |
− | Every RFNoC block, including the Radio Core, includes one command queue per data stream (certain blocks can pass multiple data streams). The depth of this command queue varies from device to device is determined at FPGA compilation time based on user settings and available resources. An overflow of the command queue will result in a system halt and often requires a physical reset of the FPGA. Here are the default command queue depths for various USRP models:
| |
− |
| |
− |
| |
− | {| class="wikitable" style="margin:auto;"
| |
− | !USRP
| |
− | !FPGA
| |
− | !Default CMD Queue Depth (Radio Core)
| |
− | !Default CMD Queue Depth (RFNoC Blocks)
| |
− | |-
| |
− |
| |
− | |X300
| |
− | |Kintex 7
| |
− | |0
| |
− | |0
| |
− | |-
| |
− |
| |
− | |X310
| |
− | |Kintex 7
| |
− | |0
| |
− | |0
| |
− | |-
| |
− |
| |
− | |E310/E312/E313
| |
− | |Zynq
| |
− | |0
| |
− | |0
| |
− | |-
| |
− |
| |
− | |E320
| |
− | |Zynq
| |
− | |0
| |
− | |0
| |
− | |-
| |
− |
| |
− | |N300/N310/N320/N321
| |
− | |Zynq
| |
− | |0
| |
− | |0
| |
− | |-
| |
− |
| |
− | |B200/B200mini
| |
− | |Spartan 6
| |
− | |0
| |
− | |0
| |
− | |-
| |
− |
| |
− | |B210/B205mini
| |
− | |Spartan 6
| |
− | |0
| |
− | |0
| |
− | |-
| |
− |
| |
− | |N200
| |
− | |Spartan
| |
− | |0
| |
− | |0
| |
− | |-
| |
− |
| |
− | |N210
| |
− | |Spartan
| |
− | |0
| |
− | |0
| |
− | |-
| |
− |
| |
− | |}
| |
− | <center>Table 1 - Default command queue depth of various USRPs</center>
| |
− |
| |
− | ===Radio Core Block Timing===
| |
− |
| |
− | The Radio Core is the heart of the USRP's functionality. The radio core is responsible for controlling all TX and RX daughterboard components (synthesizer, signal path, gain and attenuation elements, etc.), GPIO, setting up data streaming to/from DACs and ADCs, and related error handling.
| |
− |
| |
− | [[File:rfnoc with ubx.png|1200px|center]]
| |
− |
| |
− | In addition to the functionality listed above, the Radio Core is also responsible for maintaining the USRP's sense of absolute time. This absolute time value is stored as a 64-bit counter called the "VITA time" or <code>vita_time</code> in UHD. The <code>vita_time</code> counter is incremented off of the FPGA's base clock and local to the Radio Core USRP, meaning that other RFNoC blocks can not reference this counter value directly. This sense of absolute time is a subtle yet important difference between the Radio Core and other RFNoC blocks, which can also execute timed commands but do so based on the timestamps (and sample rate) of packets which pass through these blocks.
| |
− |
| |
− | ===General RFNoC Block Timing===
| |
− | The next case to cover is the handling of timed commands within a "General" RFNoC block. The Digital Down Converter (DDC) and Digital Up Converter (DUC) are two default RFNoC blocks that would be considered general RFNoC blocks. These blocks are responsible for performing digital frequency shifts on IQ samples that are passed through them. Precise execution of these frequency shifts are essential to phase coherent operation of the USRP.
| |
− |
| |
− | RFNoC blocks like the DDC and DUC are reliant on the timestamp of packets (and the sample rate of the radio) to derive a sense of time. In the case of the receive chain, samples that are digitized by an ADC are then packaged into a CHDR Packet by the Radio Core (with an included timestamp equal to <code>vita_time</code>) and are then passed downstream to the DDC. The DDC will read the timestamps of the incoming samples and apply any queued frequency shift precisely on the first sample with a timestamp >= the command time.
| |
− |
| |
− | With this method, a USRP can begin receiving data at absolute time <code>t0</code>, tune it's LO at an absolute time of <code>t1</code>, and then apply a frequency shift in the DDC <u>at the point in data</u> corresponding to <code>t1</code>, resulting in a physical and digital frequency change that begin on the exact same sample.
| |
− |
| |
− | The reverse process holds true for the transmit chain. One often overlooked difference is that the host must pass along at least 1 sample with a timestamp included in the metadata. Without this timestamped packet, the DUC can not derive a sense of time and therefore will never execute timed commands that are in its queue. This will either result in a command queue overflow or a "No Response Packet" runtime error from <code>ctrl_iface.cpp</code>.
| |
− |
| |
− | ===Miscellaneous Timed Command Notes===
| |
− |
| |
− | ====Types of timed commands====
| |
− | * Many timed commands amount to peeks and pokes to FPGA registers. Examples include gain changes, tune requests, etc.
| |
− | * Timed commands for transmitting data are a bit more involved, requiring queueing of samples, and arming of the device for a timed transmission.
| |
− |
| |
− | ====Timed commands on AD93xx-based devices====
| |
− | *Timed commands are supported on AD93xx-based USRPs, with a few caveats.
| |
− | **Timed commands do not allow for phase resync of the AD93xx internal LO.
| |
− | **All functions that do not directly interact with the AD93xx (tuning, gain change, etc) operate as you'd expect on USRPs with a dedicated daughterboard.
| |
− | *Timed commands for TX and RX on AD936x devices are supported, with caveats:
| |
− | **There will be a delay between an absolute time passing and the AD936x actually beginning an operation (TX/RX/Gain/Tune). This delay is deterministic if characterized ''for a given command''.
| |
− | **SPI communication between the FPGA and RFIC means that the delay between an absolute time passing varies depending on the timed command issued.
| |
− | **This is to say that a timed command to begin TX and RX at the same time, on an AD936x-based device (which is in loopback) should result in a deterministic delay between TX and RX down to the sample.
| |
− |
| |
− | ===Summary===
| |
− | Modern USRPs pass packets using the CHDR protocol. These packets can be used to issue commands to the USRP and may have an associated timestamp. A command with an included timestamp is called a timed command and it's important to understand how the USRP handles these timed commands. All blocks in a USRP's FPGA have a command queue and maintain a sense of time, however the Radio Core has the unique ability to store an absolute sense of time known as as the <code>vita_time</code>. When timed commands are issued to the USRP, they are added to a command FIFO of finite depth and are executed when the timestamp in the header of the command packet is >= the RFNoC block's sense of time. Utilizing these timed commands correctly allows for various USRP functionality to be executed with nanosecond precision, enabling time and phase coherent operation across multiple devices.
| |
− |
| |
− | ==UHD API for Timed Commands==
| |
− |
| |
− | ===Overview of Relevant Methods===
| |
− |
| |
− | ===Commands that can be timed===
| |
− |
| |
− | ===Time Resolution of Timed Commands===
| |
− |
| |
− | ==Example: Using Timed Commands to Phase Align Channels==
| |
− |
| |
− | ==Example: Using Timed Commands to Control GPIO==
| |
− |
| |
− | [[Category:Application Notes]]
| |