aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/include/uhdlib/usrp/common
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/include/uhdlib/usrp/common')
-rw-r--r--host/lib/include/uhdlib/usrp/common/lmx2572.hpp102
-rw-r--r--host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp42
-rw-r--r--host/lib/include/uhdlib/usrp/common/rpc.py51
-rw-r--r--host/lib/include/uhdlib/usrp/common/x400_rfdc_control.hpp85
4 files changed, 273 insertions, 7 deletions
diff --git a/host/lib/include/uhdlib/usrp/common/lmx2572.hpp b/host/lib/include/uhdlib/usrp/common/lmx2572.hpp
new file mode 100644
index 000000000..600153f1a
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/lmx2572.hpp
@@ -0,0 +1,102 @@
+//
+// Copyright 2020 Ettus Research, A National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#pragma once
+
+#include <uhd/types/time_spec.hpp>
+#include <functional>
+#include <memory>
+
+//! Control interface for an LMX2572 synthesizer
+class lmx2572_iface
+{
+public:
+ using sptr = std::shared_ptr<lmx2572_iface>;
+
+ virtual ~lmx2572_iface() = default;
+
+ enum output_t { RF_OUTPUT_A, RF_OUTPUT_B };
+
+ enum mux_in_t { DIVIDER, VCO, HIGH_IMPEDANCE, SYSREF };
+
+ //! Category of phase sync procedure. See Section 8.1.6 ("Application for
+ // SYNC") in the datasheet. Category NONE applies when no phase
+ // synchronization is required.
+ enum sync_cat { CAT1A, CAT1B, CAT2, CAT3, CAT4, NONE };
+
+ //! Write functor: Take address / data pair, craft SPI transaction
+ using write_fn_t = std::function<void(uint8_t, uint16_t)>;
+
+ //! Read functor: Return value given address
+ using read_fn_t = std::function<uint16_t(uint8_t)>;
+
+ //! Sleep functor: sleep for the specified time
+ using sleep_fn_t = std::function<void(const uhd::time_spec_t&)>;
+
+ //! Factory
+ //
+ // \param write SPI write function object
+ // \param read SPI read function object
+ // \param sleep sleep function object
+ static sptr make(write_fn_t&& poke16, read_fn_t&& peek16, sleep_fn_t&& sleep);
+
+ //! Save state to chip
+ virtual void commit() = 0;
+
+ //! Get enabled status
+ virtual bool get_enabled() = 0;
+
+ //! Enable/disable
+ virtual void set_enabled(const bool enabled = true) = 0;
+
+ //! Performs a reset of the LMX2572 by using the software reset register
+ virtual void reset() = 0;
+
+ //! Returns True if the PLL is locked, False otherwise.
+ virtual bool get_lock_status() = 0;
+
+ //! Enables or disables the phase synchronization
+ //
+ // NOTE: This does not write anything to the device, it just sets the
+ // VCO_PHASE_SYNC_EN high.
+ virtual void set_sync_mode(const bool enable) = 0;
+
+ //! Returns the enabled/disabled state of the phase synchronization
+ virtual bool get_sync_mode() = 0;
+
+ //! Enables or disables the output on both ports
+ virtual void set_output_enable_all(const bool enable) = 0;
+
+ //! Sets output A or B (OUTA_PD or OUTB_PD)
+ virtual void set_output_enable(const output_t output, const bool enable) = 0;
+
+ //! Sets the output power
+ //
+ // \param output Choose which output to control
+ // \param power Power control bits. Higher values mean more power, but the
+ // function that maps power control bits to power is non-linear,
+ // and it is also frequency-dependent. For more detail, check
+ // the data sheet, section 8.1.5.1. Ballpark numbers: 0 dBm is
+ // at about power==27, over 35 the increase becomes "not obvious".
+ virtual void set_output_power(const output_t output, const uint8_t power) = 0;
+
+ //! Sets the OUTA_MUX or OUTB_MUX input
+ virtual void set_mux_input(const output_t output, const mux_in_t input) = 0;
+
+ //! Set the output frequency
+ //
+ // A note on phase synchronization: If set_sync_mode(true) was called
+ // previously, then this method will set up the PLL in a phase-sync mode.
+ // However, this specific implementation assumes that the SYNC pin is
+ // populated, and will be pulsed after calling this command.
+ //
+ // \param target_freq The target frequency
+ // \param ref_freq The input reference frequency
+ // \param spur_dodging Set to true to enable spur dodging
+ virtual double set_frequency(const double target_freq,
+ const double ref_freq,
+ const bool spur_dodging) = 0;
+};
diff --git a/host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp b/host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp
index 98e7f2ac4..a40398991 100644
--- a/host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp
+++ b/host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp
@@ -6,8 +6,11 @@
#pragma once
+#include <uhd/features/ref_clk_calibration_iface.hpp>
#include <uhd/rfnoc/mb_controller.hpp>
#include <uhdlib/usrp/common/rpc.hpp>
+#include <uhdlib/features/discoverable_feature_registry.hpp>
+#include <uhdlib/features/fpga_load_notification_iface.hpp>
#include <uhdlib/utils/rpc.hpp>
#include <memory>
@@ -19,7 +22,8 @@ namespace uhd { namespace rfnoc {
*
* This motherboard controller abstracts out a bunch of RPC calls.
*/
-class mpmd_mb_controller : public mb_controller
+class mpmd_mb_controller : public mb_controller,
+ public ::uhd::features::discoverable_feature_registry
{
public:
using sptr = std::shared_ptr<mpmd_mb_controller>;
@@ -113,6 +117,42 @@ private:
//! Cache of available GPIO sources
std::vector<std::string> _gpio_banks;
std::unordered_map<std::string, std::vector<std::string>> _gpio_srcs;
+
+public:
+ /*! When the FPGA is reloaded, pass the notification to every Radio block
+ * Public to allow other classes to register for notifications.
+ */
+ class fpga_onload : public uhd::features::fpga_load_notification_iface {
+ public:
+ using sptr = std::shared_ptr<fpga_onload>;
+
+ fpga_onload();
+
+ void onload() override;
+
+ void request_cb(uhd::features::fpga_load_notification_iface::sptr handler);
+
+ private:
+ std::vector<std::weak_ptr<uhd::features::fpga_load_notification_iface>> _cbs;
+ };
+
+ //! Class to expose the ref_clk_calibration discoverable feature functions.
+ class ref_clk_calibration : public uhd::features::ref_clk_calibration_iface {
+ public:
+ using sptr = std::shared_ptr<ref_clk_calibration>;
+
+ ref_clk_calibration(uhd::usrp::mpmd_rpc_iface::sptr rpcc);
+
+ void set_ref_clk_tuning_word(uint32_t tuning_word) override;
+ uint32_t get_ref_clk_tuning_word() override;
+ void store_ref_clk_tuning_word(uint32_t tuning_word) override;
+
+ private:
+ uhd::usrp::mpmd_rpc_iface::sptr _rpcc;
+ };
+
+ fpga_onload::sptr _fpga_onload;
+ ref_clk_calibration::sptr _ref_clk_cal;
};
}} // namespace uhd::rfnoc
diff --git a/host/lib/include/uhdlib/usrp/common/rpc.py b/host/lib/include/uhdlib/usrp/common/rpc.py
index 04a43ebce..4ca30b07d 100644
--- a/host/lib/include/uhdlib/usrp/common/rpc.py
+++ b/host/lib/include/uhdlib/usrp/common/rpc.py
@@ -9,7 +9,7 @@ import sys
from mako.template import Template
class Function:
- def __init__(self, return_type, function_name, args):
+ def __init__(self, return_type, function_name, args, no_claim=False):
self.name = function_name
self.does_return = return_type != "void"
self.return_type = return_type
@@ -17,6 +17,7 @@ class Function:
self.rpcname = f"\"{function_name}\""
self.args = [" ".join(arg) for arg in args]
self.has_rpcprefix = False
+ self.no_claim = no_claim
def enable_rpcprefix(self):
self.rpcname = f"_rpc_prefix + \"{self.name}\""
@@ -31,7 +32,7 @@ class Interface:
for fn in self.functions:
fn.enable_rpcprefix()
-def fn_from_string(function_string):
+def fn_from_string(function_string, no_claim=False):
m = re.match(r"^([a-zA-Z:<>,_0-9 ]+)\s+([a-zA-Z0-9_]+)\(([a-zA-Z0-9,_:&<> ]*)\)$", function_string)
return_type = m.group(1)
function_name = m.group(2)
@@ -39,7 +40,7 @@ def fn_from_string(function_string):
args = [arg.strip() for arg in args.split(",")]
args = [arg.split(" ") for arg in args if len(arg) > 0]
args = [(" ".join(arg[:-1]), arg[-1]) for arg in args]
- return Function(return_type, function_name, args)
+ return Function(return_type, function_name, args, no_claim)
IFACES = [
Interface("mpmd_rpc", [
@@ -66,7 +67,36 @@ IFACES = [
fn_from_string("std::map<std::string, std::string> get_mb_eeprom()"),
fn_from_string("std::vector<std::string> get_gpio_src(const std::string& bank)"),
fn_from_string("void set_gpio_src(const std::string& bank, const std::vector<std::string>& src)"),
+
+ # ref_clk_calibration
+ fn_from_string("void set_ref_clk_tuning_word(uint32_t tuning_word)"),
+ fn_from_string("uint32_t get_ref_clk_tuning_word()"),
+ fn_from_string("void store_ref_clk_tuning_word(uint32_t tuning_word)"),
+ ]),
+ Interface("x400_rpc", [
+ fn_from_string("std::vector<std::map<std::string, std::string>> get_dboard_info()", no_claim=True),
+ fn_from_string("void set_cal_frozen(bool state, size_t block_count, size_t chan)"),
+ fn_from_string("std::vector<int> get_cal_frozen(size_t block_count, size_t chan)"),
+ fn_from_string("double rfdc_set_nco_freq(const std::string& trx, size_t block_count, size_t chan, double freq)"),
+ fn_from_string("double rfdc_get_nco_freq(const std::string& trx, size_t block_count, size_t chan)"),
+ fn_from_string("double get_master_clock_rate()"),
+ fn_from_string("std::map<std::string, std::vector<uint8_t>> get_db_eeprom(size_t db_idx)"),
+ fn_from_string("bool get_threshold_status(size_t db_number, size_t chan, size_t threshold_block)"),
+ fn_from_string("void set_dac_mux_enable(size_t motherboard_channel_number, int enable)"),
+ fn_from_string("void set_dac_mux_data(size_t i, size_t q)"),
+ fn_from_string("double get_spll_freq()"),
+ fn_from_string("void setup_threshold(size_t db_number, size_t chan, size_t threshold_block, const std::string& mode, size_t delay, size_t under, size_t over)"),
+ fn_from_string("bool is_db_gpio_ifc_present(size_t db_idx)"),
]),
+ Interface("dboard_base_rpc", [
+ fn_from_string("std::vector<std::string> get_sensors(const std::string& trx)"),
+ fn_from_string("sensor_value_t::sensor_map_t get_sensor(const std::string& trx, const std::string& sensor, size_t chan)"),
+ ], has_rpcprefix=True),
+ Interface("zbx_rpc", [
+ fn_from_string("double get_dboard_prc_rate()"),
+ fn_from_string("double get_dboard_sample_rate()"),
+ fn_from_string("void enable_iq_swap(bool is_band_inverted, const std::string& trx, size_t chan)"),
+ ], has_rpcprefix=True),
]
COMMON_TMPL = """<% import time %>\
@@ -117,11 +147,20 @@ namespace uhd { namespace usrp {
%for function in iface.functions:
${function.return_type} ${function.name}(${",".join(function.args)}) override
{
- %if function.does_return:
- return _rpcc->request_with_token<${function.return_type}>(${",".join([function.rpcname] + function.arg_names)});
+ %if function.no_claim:
+ %if function.does_return:
+ return _rpcc->request<${function.return_type}>
+ %else:
+ _rpcc->notify
+ %endif
%else:
- _rpcc->notify_with_token(${",".join([function.rpcname] + function.arg_names)});
+ %if function.does_return:
+ return _rpcc->request_with_token<${function.return_type}>
+ %else:
+ _rpcc->notify_with_token
+ %endif
%endif
+ (${",".join([function.rpcname] + function.arg_names)});
}
%endfor
diff --git a/host/lib/include/uhdlib/usrp/common/x400_rfdc_control.hpp b/host/lib/include/uhdlib/usrp/common/x400_rfdc_control.hpp
new file mode 100644
index 000000000..8d2923436
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/x400_rfdc_control.hpp
@@ -0,0 +1,85 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#pragma once
+
+#include <uhd/types/memmap_iface.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <cstdint>
+#include <memory>
+#include <vector>
+#include <string>
+
+namespace uhd { namespace rfnoc { namespace x400 {
+
+//! Control class for the RFDC components of a single daughterboard
+//
+// This class controls the NCOs and other RFDC settings. The corresponding FPGA
+// module is rfdc_timing_control.v.
+class rfdc_control
+{
+public:
+ using sptr = std::shared_ptr<rfdc_control>;
+
+ struct regmap
+ {
+ //! Address of the NCO reset register
+ static constexpr uint32_t NCO_RESET = 0;
+ //! Bit position of reset-start bit (w)
+ static constexpr uint32_t NCO_RESET_START_MSB = 0;
+ //! Bit position of reset-done bit (r)
+ static constexpr uint32_t NCO_RESET_DONE_MSB = 1;
+ //! Address of the gearbox reset register
+ static constexpr uint32_t GEARBOX_RESET = 4;
+ //! Bit position of ADC gearbox reset
+ static constexpr uint32_t ADC_RESET_MSB = 0;
+ //! Bit position of DAC gearbox reset
+ static constexpr uint32_t DAC_RESET_MSB = 1;
+ };
+
+ //! Identify the NCOs/ADCs/DACs available to this radio control
+ enum class rfdc_type { RX0, RX1, TX0, TX1 };
+
+ rfdc_control(uhd::memmap32_iface_timed&& iface, const std::string& log_id);
+
+ //! Reset the listed NCOs
+ //
+ // All NCOs that are listed in \p ncos are reset synchronously.
+ //
+ // \param ncos A list of NCOs that shall be reset at the given time
+ // \param time The time at which the reset shall occur
+ void reset_ncos(const std::vector<rfdc_type>& ncos, const uhd::time_spec_t& time);
+
+ //! Reset the listed gearboxes
+ //
+ // All gearboxes that are listed in \p gearboxes are reset synchronously.
+ //
+ // \param gearboxes A list of gearboxes that shall be reset at the given time
+ // \param time The time at which the reset shall occur. Note: If \p time is
+ // set to ASAP, the resets will still occur synchronously, but
+ // at a non-deterministic time. This will suffice for synchronizing
+ // gearboxes on a single device.
+ void reset_gearboxes(
+ const std::vector<rfdc_type>& gearboxes, const uhd::time_spec_t& time);
+
+ //! Return true if the NCO is out of reset
+ bool get_nco_reset_done();
+
+ //! Set an NCO to a specific frequency
+ //
+ // \param nco Which NCO to re-tune
+ // \param freq the new frequency to tune it to (in Hz)
+ double set_nco_freq(const rfdc_type nco, const double freq);
+
+private:
+ //! Peek/poke interface
+ memmap32_iface_timed _iface;
+
+ //! Prefix for log messages
+ const std::string _log_id;
+};
+
+}}} // namespace uhd::rfnoc::x400