aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2017-06-05 16:08:44 -0700
committerMartin Braun <martin.braun@ettus.com>2017-12-22 15:03:59 -0800
commit14f0e113d967c9a9098be8cfea7f333c4bf5373f (patch)
tree79c07977235466a1c033fd925ce3aafd264acdc4 /host/lib/usrp
parent45012fa48cd82fcc757ca46ce9ee270e670f590f (diff)
downloaduhd-14f0e113d967c9a9098be8cfea7f333c4bf5373f.tar.gz
uhd-14f0e113d967c9a9098be8cfea7f333c4bf5373f.tar.bz2
uhd-14f0e113d967c9a9098be8cfea7f333c4bf5373f.zip
eiscat: Added more APIs for more fine-grained control
Diffstat (limited to 'host/lib/usrp')
-rw-r--r--host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp207
-rw-r--r--host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp46
2 files changed, 199 insertions, 54 deletions
diff --git a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp
index 5a7a5907f..ea1e31102 100644
--- a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp
+++ b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp
@@ -69,7 +69,9 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl)
UHD_LOG_TRACE("EISCAT", "eiscat_radio_ctrl_impl::ctor() ");
_num_ports = get_output_ports().size();
UHD_LOG_TRACE("EISCAT", "Number of channels: " << _num_ports);
- UHD_LOG_TRACE("EISCAT", "Setting tick rate to " << EISCAT_TICK_RATE/1e6 << " MHz");
+ UHD_LOG_TRACE("EISCAT",
+ "Tick rate is " << EISCAT_TICK_RATE/1e6 << " MHz"
+ );
/**** Configure the radio_ctrl itself ***********************************/
radio_ctrl_impl::set_rate(EISCAT_TICK_RATE);
@@ -80,18 +82,72 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl)
radio_ctrl_impl::set_rx_bandwidth(EISCAT_DEFAULT_BANDWIDTH, chan);
}
- /**** Configure the digital gain controls *******************************/
- for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) {
- _tree->access<double>(get_arg_path("gain", i) / "value")
- .set_coercer([](double gain){ return std::max(-1.0, std::min(1.0, gain)); })
- .add_coerced_subscriber([this, i](double gain){
- this->set_antenna_gain(i, gain);
- })
- .set(EISCAT_DEFAULT_NORM_GAIN)
+ /**** Set up arg-based control API **************************************/
+ // None of these properties are defined in the XML file. Some of them have
+ // non-Noc-Script-compatible types.
+ _tree->create<bool>(get_arg_path("sysref", 0) / "value")
+ .set(true)
+ .add_coerced_subscriber([this](bool){
+ try {
+ this->send_sysref();
+ } catch (const uhd::exception &ex) {
+ UHD_LOGGER_WARNING("EISCAT")
+ << "Failed to send SYSREF: " << ex.what();
+ throw uhd::runtime_error(str(
+ boost::format("Failed to send SYSREF: %s")
+ % ex.what()
+ ));
+ }
+ })
+ .set_publisher([](){ return true; })
+ ;
+ _tree->create<bool>(get_arg_path("assert_adcs_deframers", 0) / "value")
+ .set(true)
+ .set_publisher([this](){ return this->assert_adcs_deframers(); })
+ ;
+ _tree->create<bool>(get_arg_path("assert_deframer_status", 0) / "value")
+ .set(true)
+ .set_publisher([this](){ return this->assert_adcs_deframers(); })
+ ;
+ _tree->create<time_spec_t>(get_arg_path("fir_ctrl_time", 0) / "value")
+ .add_coerced_subscriber([this](time_spec_t switch_time){
+ this->set_fir_ctrl_time(switch_time);
+ })
+ .set(time_spec_t(0.0))
+ ;
+ for (size_t beam = 0; beam < EISCAT_NUM_BEAMS; beam++) {
+ for (size_t ant = 0; ant < EISCAT_NUM_ANTENNAS; ant++) {
+ const size_t fir_index = beam * EISCAT_NUM_ANTENNAS + ant;
+ // These are not in the XML file
+ _tree->create<int>(get_arg_path("fir_select", fir_index) / "value")
+ .add_coerced_subscriber([beam, ant, this](const size_t ram_idx){
+ UHD_ASSERT_THROW(ram_idx < EISCAT_NUM_FIR_SETS);
+ this->select_filter(
+ beam,
+ ant,
+ ram_idx,
+ this->get_arg<time_spec_t>("fir_ctrl_time", 0),
+ false
+ );
+ })
+ ;
+ }
+ }
+ for (size_t fir_set = 0; fir_set < EISCAT_NUM_FIR_SETS; fir_set++) {
+ _tree->create<std::vector<fir_tap_t>>(
+ get_arg_path("fir_taps", fir_set) / "value")
+ .add_coerced_subscriber(
+ [this, fir_set](const std::vector<fir_tap_t> &taps){
+ this->write_fir_taps(fir_set, taps);
+ }
+ )
;
}
+
/**** Add subscribers for our special properties ************************/
+ // The difference between this block and the previous that these *are*
+ // defined in the XML file, and can have defaults set there.
_tree->access<int>(get_arg_path("choose_beams", 0) / "value")
.add_coerced_subscriber([this](int choose_beams){
this->set_beam_selection(choose_beams);
@@ -116,20 +172,29 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl)
}) // No update!
;
- /**** Set up legacy compatible properties ******************************/
+ /**** Configure the digital gain controls *******************************/
+ for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) {
+ _tree->access<double>(get_arg_path("gain", i) / "value")
+ .set_coercer([](double gain){
+ return std::max(-1.0, std::min(1.0, gain));
+ })
+ .add_coerced_subscriber([this, i](double gain){
+ this->set_antenna_gain(i, gain);
+ })
+ .update()
+ ;
+ }
+
+ /**** Set up legacy compatible properties *******************************/
// For use with multi_usrp APIs etc.
// For legacy prop tree init:
fs_path fe_path = fs_path("dboards") / "A" / "rx_frontends";
- auto antenna_options = std::vector<std::string>{"BF"};
- for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) {
- antenna_options.push_back(str(boost::format("Rx%d") % i));
- }
-
- // The EISCAT dboards have 16 frontends total, but they map to 5 channels
- // each through a matrix of FIR filters and summations. UHD will get much
- // less confused if we create 5 fake frontends, because that's also the
- // number of channels. Since we have no control over the frontends,
+ // The EISCAT dboards have 16 frontends total, but they map to 10 beams
+ // each through a matrix of FIR filters and summations, and then only 5 of
+ // those channels go out through the Noc-Shell.
+ // UHD will thus get much less confused if we create 5 fake frontends (i.e.,
+ // number of Noc-Block-ports). Since we have no control over the frontends,
// nothing is lost here.
for (size_t fe_idx = 0; fe_idx < _num_ports; fe_idx++) {
_tree->create<std::string>(fe_path / fe_idx / "name")
@@ -138,13 +203,6 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl)
_tree->create<std::string>(fe_path / fe_idx / "connection")
.set("I")
;
- _tree->create<std::string>(fe_path / fe_idx / "antenna" / "value")
- .add_coerced_subscriber(boost::bind(&eiscat_radio_ctrl_impl::set_rx_antenna, this, _1, 0))
- .set_publisher(boost::bind(&radio_ctrl_impl::get_rx_antenna, this, 0))
- ;
- _tree->create<std::vector<std::string>>(fe_path / fe_idx / "antenna" / "options")
- .set(antenna_options)
- ;
_tree->create<double>(fe_path / fe_idx / "freq" / "value")
.set(EISCAT_CENTER_FREQ)
//.set_coercer(boost::bind(&eiscat_radio_ctrl_impl::set_rx_frequency, this, _1, 0))
@@ -174,6 +232,27 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl)
;
}
+ auto antenna_options = std::vector<std::string>{"BF"};
+ for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i++) {
+ antenna_options.push_back(str(boost::format("Rx%d") % i));
+ antenna_options.push_back(str(boost::format("BF%d") % i));
+ }
+ for (size_t beam_idx = 0; beam_idx < _num_ports; beam_idx++) {
+ _tree->create<std::string>(fe_path / beam_idx / "antenna" / "value")
+ .set(EISCAT_DEFAULT_ANTENNA)
+ .add_coerced_subscriber([this, beam_idx](const std::string &name){
+ this->set_rx_antenna(name, beam_idx);
+ })
+ .set_publisher([this, beam_idx](){
+ return this->get_rx_antenna(beam_idx);
+ })
+ ;
+ _tree->create<std::vector<std::string>>(
+ fe_path / beam_idx / "antenna" / "options")
+ .set(antenna_options)
+ ;
+ }
+
// We can actually stream data to an EISCAT board, so it needs some tx
// frontends too:
fe_path = fs_path("dboards") / "A" / "tx_frontends";
@@ -187,8 +266,8 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl)
// when we reach this line:
UHD_ASSERT_THROW(not _tree->exists("tick_rate"));
_tree->create<double>("tick_rate")
- //.set_coercer(boost::bind(&eiscat_radio_ctrl_impl::set_rate, this, _1))
.set(EISCAT_TICK_RATE)
+ .set_coercer(boost::bind(&eiscat_radio_ctrl_impl::set_rate, this, _1))
;
if (not _tree->exists(fs_path("clock_source/value"))) {
@@ -396,24 +475,21 @@ void eiscat_radio_ctrl_impl::set_rpc_client(
const uhd::device_addr_t &block_args
) {
_rpcc = rpcc;
- std::function<void()> send_sysref;
- if (block_args.has_key("use_mpm_sysref")) {
- send_sysref = [rpcc](){ rpcc->notify("db_0_send_sysref"); };
- } else {
- send_sysref = [this](){ this->sr_write("SR_SYSREF", 1); };
- }
+ _block_args = block_args;
UHD_LOG_INFO(
"EISCAT",
"Finalizing dboard initialization using internal PPS"
);
send_sysref();
- rpcc->request_with_token<bool>("db_0_init_adcs_and_deframers");
- rpcc->request_with_token<bool>("db_1_init_adcs_and_deframers");
+ if (not assert_adcs_deframers()) {
+ throw uhd::runtime_error("Failed to initialize ADCs and JESD cores!");
+ }
send_sysref();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
- rpcc->request_with_token<bool>("db_0_check_deframer_status");
- rpcc->request_with_token<bool>("db_1_check_deframer_status");
+ if (not assert_deframer_status()) {
+ throw uhd::runtime_error("Failed to finalize JESD core setup!");
+ }
}
/****************************************************************************
@@ -458,7 +534,8 @@ void eiscat_radio_ctrl_impl::select_filter(
const size_t beam_index,
const size_t antenna_index,
const size_t fir_index,
- const uhd::time_spec_t &time_spec
+ const uhd::time_spec_t &time_spec,
+ const bool write_time
) {
if (antenna_index >= EISCAT_NUM_ANTENNAS) {
throw uhd::value_error(str(
@@ -468,8 +545,10 @@ void eiscat_radio_ctrl_impl::select_filter(
}
if (beam_index >= EISCAT_NUM_BEAMS) {
throw uhd::value_error(str(
- boost::format("Beam index %d out of range. There are %d beam channels in EISCAT.")
- % beam_index % EISCAT_NUM_BEAMS
+ boost::format("Beam index %d out of range. "
+ "There are %d beam channels in EISCAT.")
+ % beam_index
+ % EISCAT_NUM_BEAMS
));
}
@@ -487,18 +566,35 @@ void eiscat_radio_ctrl_impl::select_filter(
;
if (not send_now) {
- uint64_t cmd_time_ticks = time_spec.to_ticks(EISCAT_TICK_RATE);
UHD_LOG_TRACE("EISCAT", str(
- boost::format("Filter selection will be applied at time %f (0x%016X == %u)")
+ boost::format("Filter selection will be applied at "
+ "time %f (0x%016X == %u). %s")
% time_spec.get_full_secs()
- % cmd_time_ticks
+ % time_spec.to_ticks(EISCAT_TICK_RATE)
+ % (write_time ? "Writing time regs now."
+ : "Assuming time regs already up-to-date.")
));
- sr_write("SR_FIR_COMMANDS_CTRL_TIME_LO", cmd_time_ticks & 0xFFFFFFFF);
- sr_write("SR_FIR_COMMANDS_CTRL_TIME_HI", (cmd_time_ticks >> 32) & 0xFFFFFFFF);
+ if (write_time) {
+ set_fir_ctrl_time(time_spec);
+ }
}
sr_write("SR_FIR_COMMANDS_RELOAD", reg_value);
}
+void eiscat_radio_ctrl_impl::set_fir_ctrl_time(
+ const uhd::time_spec_t &time_spec
+) {
+ const uint64_t cmd_time_ticks = time_spec.to_ticks(EISCAT_TICK_RATE);
+ sr_write(
+ "SR_FIR_COMMANDS_CTRL_TIME_LO",
+ uint32_t(cmd_time_ticks & 0xFFFFFFFF)
+ );
+ sr_write(
+ "SR_FIR_COMMANDS_CTRL_TIME_HI",
+ uint32_t((cmd_time_ticks >> 32) & 0xFFFFFFFF)
+ );
+}
+
void eiscat_radio_ctrl_impl::set_antenna_gain(
const size_t antenna_idx,
const double normalized_gain
@@ -567,6 +663,15 @@ void eiscat_radio_ctrl_impl::enable_firs(bool enable)
configure_beams(new_value);
}
+void eiscat_radio_ctrl_impl::send_sysref()
+{
+ if (_block_args.has_key("use_mpm_sysref")) {
+ _rpcc->notify_with_token("db_0_send_sysref");
+ } else {
+ this->sr_write("SR_SYSREF", 1);
+ }
+}
+
void eiscat_radio_ctrl_impl::enable_counter(bool enable)
{
const uint32_t old_value = user_reg_read32(RB_CHOOSE_BEAMS);
@@ -577,6 +682,18 @@ void eiscat_radio_ctrl_impl::enable_counter(bool enable)
configure_beams(new_value);
}
+bool eiscat_radio_ctrl_impl::assert_adcs_deframers()
+{
+ return _rpcc->request_with_token<bool>("db_0_init_adcs_and_deframers")
+ and _rpcc->request_with_token<bool>("db_1_init_adcs_and_deframers");
+}
+
+bool eiscat_radio_ctrl_impl::assert_deframer_status()
+{
+ return _rpcc->request_with_token<bool>("db_0_check_deframer_status")
+ and _rpcc->request_with_token<bool>("db_1_check_deframer_status");
+}
+
/****************************************************************************
* Registry
***************************************************************************/
diff --git a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp
index 556a32611..2da01ed03 100644
--- a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp
+++ b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp
@@ -49,7 +49,7 @@ namespace uhd {
* - fir_select (int): Will queue a filter for manipulating a specific
* contribution. The value is the filter index in the BRAM.
* The port parameter specifies which filter; filters are
- * indexed 0...159 using the equation beam_index * 10 +
+ * indexed 0...159 using the equation beam_index * 16 +
* antenna_idx. Example: `set_arg("fir_select", 357, 16)`
* will apply filter number 357 to the zeroth antenna for
* beam number 1 (i.e. the second beam). Returns the last
@@ -67,12 +67,12 @@ namespace uhd {
* - choose_beams (int): Configures beam selection (upper, lower, are neighbour
* contributions included). See set_beam_selection() for
* details.
- * - enable_firs (bool): Can be used to disable fir FIR matrix. This routes the
- * JESD output directly to the noc_shell.
- * - enable_counter (bool): If the feature is available in the given FPGA image,
- * setting this to true will disable the JESD core
- * output and will input a counter signal (ramp)
- * instead.
+ * - enable_firs (int): Can be used to disable fir FIR matrix. This routes the
+ * JESD output directly to the noc_shell.
+ * - enable_counter (int): If the feature is available in the given FPGA image,
+ * setting this to true will disable the JESD core
+ * output and will input a counter signal (ramp)
+ * instead.
* - configure_beams (int): Danger, danger: Directly writes the
* SR_BEAMS_TO_NEIGHBOR register. Writing this can put
* some of the other properties out of sync, because
@@ -200,15 +200,32 @@ private:
* \param beam_index Beam index
* \param antenna_index Antenna index
* \param fir_index The index of the FIR filter taps that get applied
- * \param time_spec If non-zero, the taps get applied at this time
+ * \param time_spec If non-zero, the taps get applied at this time.
+ * Otherwise, they get sent out now.
+ * \param write_time If false, time will never get written *even if* it is
+ * non-zero. The assumption is that someone else wrote
+ * the value previously
+ * \param write_time If false, time will never get written *even if* it is
+ * non-zero. The assumption is that someone else wrote
+ * the value previously
*/
void select_filter(
const size_t beam_index,
const size_t antenna_index,
const size_t fir_index,
- const uhd::time_spec_t &time_spec
+ const uhd::time_spec_t &time_spec,
+ const bool write_time=true
);
+
+ /*! Sets the command time for the next call to select_filter()
+ *
+ * \param time_spec This value gets written to the FPGA and is applied to
+ * *all* subsequent filter selections. To request
+ * immediate application of filters, set this to zero.
+ */
+ void set_fir_ctrl_time(const uhd::time_spec_t &time_spec);
+
/*! Sets the digital gain on a specific antenna
*
* \param antenna_idx Antenna for which this gain setting applies
@@ -244,6 +261,14 @@ private:
*/
void enable_counter(bool enable);
+ void send_sysref();
+
+ //! Run initialization of ADCs and deframers; returns success status
+ bool assert_adcs_deframers();
+
+ //! Run final step of JESD core setup; returns success status
+ bool assert_deframer_status();
+
/*! The number of channels this block outputs
*
* This is *not* the number of antennas, but the number of streams a single
@@ -251,6 +276,9 @@ private:
*/
size_t _num_ports;
+ //! Additional block args; gets set during set_rpc_client()
+ uhd::device_addr_t _block_args;
+
/*! Reference to the RPC client
*/
uhd::rpc_client::sptr _rpcc;