diff options
-rw-r--r-- | host/include/uhd/rfnoc/blocks/ddc_eiscat.xml | 60 | ||||
-rw-r--r-- | host/include/uhd/rfnoc/blocks/radio_eiscat.xml | 16 | ||||
-rw-r--r-- | host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp | 244 | ||||
-rw-r--r-- | host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp | 3 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/eiscat.py | 58 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py | 11 |
6 files changed, 326 insertions, 66 deletions
diff --git a/host/include/uhd/rfnoc/blocks/ddc_eiscat.xml b/host/include/uhd/rfnoc/blocks/ddc_eiscat.xml index 6cd0f117c..df39ce891 100644 --- a/host/include/uhd/rfnoc/blocks/ddc_eiscat.xml +++ b/host/include/uhd/rfnoc/blocks/ddc_eiscat.xml @@ -25,8 +25,8 @@ </setreg> <!-- DDC block registers --> <setreg> - <!-- CORDIC phase increment word --> - <name>CORDIC_FREQ</name> + <!-- DDS phase increment word --> + <name>DDS_FREQ</name> <address>132</address> </setreg> <setreg> @@ -41,6 +41,7 @@ </setreg> <setreg> <!-- Real mode, swap IQ --> + <!-- Real mode = bit 1, swap IQ = bit 0 --> <name>MODE</name> <address>135</address> </setreg> @@ -57,6 +58,17 @@ <type>double</type> <value>0.0</value> <port>0</port> + <!--<action>--> + <!--SR_WRITE("CORDIC_FREQ", $cordic_freq)--> + <!--</action>--> + <!--FIXME Calculate this properly--> + </arg> + <arg> + <name>mode</name> + <type>int</type> + <value>2</value> + <port>0</port> + <action>SR_WRITE("MODE", $mode)</action> </arg> <arg> <name>input_rate</name> @@ -92,6 +104,17 @@ <type>double</type> <value>0.0</value> <port>1</port> + <!--<action>--> + <!--SR_WRITE("CORDIC_FREQ", $cordic_freq)--> + <!--</action>--> + <!--FIXME Calculate this properly--> + </arg> + <arg> + <name>mode</name> + <type>int</type> + <value>2</value> + <port>1</port> + <action>SR_WRITE("MODE", $mode)</action> </arg> <arg> <name>input_rate</name> @@ -127,6 +150,17 @@ <type>double</type> <value>0.0</value> <port>2</port> + <!--<action>--> + <!--SR_WRITE("CORDIC_FREQ", $cordic_freq)--> + <!--</action>--> + <!--FIXME Calculate this properly--> + </arg> + <arg> + <name>mode</name> + <type>int</type> + <value>2</value> + <port>2</port> + <action>SR_WRITE("MODE", $mode)</action> </arg> <arg> <name>input_rate</name> @@ -162,6 +196,17 @@ <type>double</type> <value>0.0</value> <port>3</port> + <!--<action>--> + <!--SR_WRITE("CORDIC_FREQ", $cordic_freq)--> + <!--</action>--> + <!--FIXME Calculate this properly--> + </arg> + <arg> + <name>mode</name> + <type>int</type> + <value>2</value> + <port>3</port> + <action>SR_WRITE("MODE", $mode)</action> </arg> <arg> <name>input_rate</name> @@ -197,6 +242,17 @@ <type>double</type> <value>0.0</value> <port>4</port> + <!--<action>--> + <!--SR_WRITE("CORDIC_FREQ", $cordic_freq)--> + <!--</action>--> + <!--FIXME Calculate this properly--> + </arg> + <arg> + <name>mode</name> + <type>int</type> + <value>2</value> + <port>4</port> + <action>SR_WRITE("MODE", $mode)</action> </arg> <arg> <name>input_rate</name> diff --git a/host/include/uhd/rfnoc/blocks/radio_eiscat.xml b/host/include/uhd/rfnoc/blocks/radio_eiscat.xml index ddf68580b..cc6fb7f3b 100644 --- a/host/include/uhd/rfnoc/blocks/radio_eiscat.xml +++ b/host/include/uhd/rfnoc/blocks/radio_eiscat.xml @@ -23,7 +23,7 @@ to output jesd streams directly [3:0] = 6 --> <name>SR_BEAMS_TO_NEIGHBOR</name> <address>202</address> - <value>2</value> + <value>14</value> </setreg> <setreg> <!--1-Bit register. Are we expecting previous contributions? 1==yes we are --> @@ -57,23 +57,23 @@ </setreg> <readback> <name>RB_NUM_TAPS</name> - <address>0</address> + <address>6</address> </readback> <readback> <name>RB_NUM_CHANNELS</name> - <address>1</address> + <address>7</address> </readback> <readback> <name>RB_NUM_BEAMS</name> - <address>2</address> + <address>8</address> </readback> <readback> <name>RB_NUM_FILTERS</name> - <address>3</address> + <address>9</address> </readback> <readback> - <name>RB_VITA_TIME</name> - <address>4</address> + <name>RB_STREAM_ENABLED</name> + <address>10</address> </readback> </registers> <!-- Args --> @@ -82,7 +82,7 @@ <arg> <name>spp</name> <type>int</type> - <value>3992</value> + <value>3968</value> </arg> <arg> <name>taps</name> 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 3912e4f32..73851656b 100644 --- a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp +++ b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.cpp @@ -22,7 +22,7 @@ using namespace uhd::rfnoc; namespace { const size_t SR_ANTENNA_GAIN_BASE = 204; const size_t SR_ANTENNA_SELECT_BASE = 192; // Note: On other dboards, 192 is DB_GPIO address space - const size_t RB_CHOOSE_BEAMS = 10; + const size_t RB_CHOOSE_BEAMS = 11; const double EISCAT_TICK_RATE = 208e6; // Hz const double EISCAT_RADIO_RATE = 104e6; // Hz @@ -33,9 +33,11 @@ namespace { const size_t EISCAT_NUM_ANTENNAS = 16; const size_t EISCAT_NUM_BEAMS = 10; const size_t EISCAT_NUM_PORTS = 5; - const size_t EISCAT_GAIN_RANGE = 18; // Bits, *signed*. - const int32_t EISCAT_MAX_GAIN = (1<<(EISCAT_GAIN_RANGE-1))-1; - const int32_t EISCAT_MIN_GAIN = -(1<<(EISCAT_GAIN_RANGE-1)); + const size_t EISCAT_MAX_GAIN_RANGE = 18; // Bits, *signed*. + const size_t EISCAT_UNIT_GAIN_RANGE = 14; // Bits, *signed*. + const int32_t EISCAT_MAX_GAIN = (1<<(EISCAT_MAX_GAIN_RANGE-1))-1; + const int32_t EISCAT_UNIT_GAIN = (1<<(EISCAT_UNIT_GAIN_RANGE-1))-1; + const int32_t EISCAT_MIN_GAIN = -(1<<(EISCAT_MAX_GAIN_RANGE-1)); const double EISCAT_DEFAULT_NORM_GAIN = 1.0; // Normalized. This is the actual digital gain value. const size_t EISCAT_BITS_PER_TAP = 18; const eiscat_radio_ctrl_impl::fir_tap_t EISCAT_MAX_TAP_VALUE = (1<<(EISCAT_BITS_PER_TAP-1))-1; @@ -70,6 +72,10 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) radio_ctrl_impl::set_rx_gain(EISCAT_DEFAULT_NULL_GAIN, chan); radio_ctrl_impl::set_rx_antenna(EISCAT_DEFAULT_ANTENNA, chan); radio_ctrl_impl::set_rx_bandwidth(EISCAT_DEFAULT_BANDWIDTH, chan); + // We might get tx async messages from upstream radios, we send them to the + // nevernever by default or they interfere with our streamers or ctrl_iface + // objects. The assumption is that FF:FF is never a valid SID. + this->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, 0xFFFF, chan); } /**** Set up arg-based control API **************************************/ @@ -169,7 +175,7 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) 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)); + return std::max(-16.0, std::min(16.0, gain)); }) .add_coerced_subscriber([this, i](double gain){ this->set_antenna_gain(i, gain); @@ -268,6 +274,28 @@ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(eiscat_radio_ctrl) ; } + for (size_t i = 0; i < EISCAT_NUM_PORTS; i++) { + _tree->create<uhd::time_spec_t>(get_arg_path("pseudo_stream_cmd", i) / "value") + .add_coerced_subscriber([this, i](uhd::time_spec_t stream_time){ + if (stream_time != uhd::time_spec_t(0.0)) { + uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + cmd.stream_now = false; + cmd.time_spec = stream_time; + this->issue_stream_cmd(cmd, i); + } else { + this->issue_stream_cmd( + uhd::stream_cmd_t(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS), + i + ); + } + }) + ; + } + //FIXME elaborate this more, but for now it works. + _tree->create<int>("rx_codecs/A/gains"); + _tree->create<std::string>("rx_codecs/A/name").set("ADS54J66"); + + // There is only ever one EISCAT radio per mboard, so this should be unset // when we reach this line: UHD_ASSERT_THROW(not _tree->exists("tick_rate")); @@ -295,7 +323,7 @@ void eiscat_radio_ctrl_impl::set_rx_antenna( const std::string &ant, const size_t port ) { - UHD_ASSERT_THROW(port < EISCAT_NUM_PORTS); + UHD_ASSERT_THROW(port < EISCAT_NUM_BEAMS); if (ant == "BF") { UHD_LOG_TRACE("EISCAT", "Setting antenna to 'BF' (which is a no-op)"); return; @@ -357,16 +385,13 @@ void eiscat_radio_ctrl_impl::set_rx_antenna( sr_write(SR_ANTENNA_SELECT_BASE + port, antenna_idx); enable_firs(false); } else if (ant_mode == "FI") { - size_t beam_select_offset = - (get_arg<int>("choose_beams") & EISCAT_CONTRIB_UPPER) ? - EISCAT_NUM_PORTS : 0; - const size_t beam_index = port + beam_select_offset; + size_t beam_index = port % EISCAT_NUM_PORTS; UHD_LOG_TRACE("EISCAT", str( boost::format("Setting port %d to filter index %d on all antennas " - "using beam index %d.") + "using beam indices %d and %d.") % port % antenna_idx - % beam_index + % beam_index % (beam_index + EISCAT_NUM_PORTS) )); // Note: antenna_idx is not indexing a physical antenna in this scenario. uhd::time_spec_t send_now(0.0); @@ -377,6 +402,50 @@ void eiscat_radio_ctrl_impl::set_rx_antenna( antenna_idx, send_now ); + select_filter( + beam_index + EISCAT_NUM_PORTS, + i, + antenna_idx, + send_now + ); + } + enable_firs(true); + } else if (ant_mode == "CN") { + const size_t beam_index = port % EISCAT_NUM_PORTS; + UHD_LOG_TRACE("EISCAT", str( + boost::format("Setting port %d to filter index %d on all antennas " + "using beam indices %d and %d.") + % port + % antenna_idx + % beam_index % (beam_index + EISCAT_NUM_PORTS) + )); + // Note: antenna_idx is not indexing a physical antenna in this scenario. + uhd::time_spec_t send_now(0.0); + for (size_t i = 0; i < EISCAT_NUM_ANTENNAS; i+=2) { + select_filter( + beam_index, + i, + 0, + send_now + ); + select_filter( + beam_index + EISCAT_NUM_PORTS, + i, + 0, + send_now + ); + select_filter( + beam_index, + i+1, + antenna_idx, + send_now + ); + select_filter( + beam_index + EISCAT_NUM_PORTS, + i+1, + antenna_idx, + send_now + ); } enable_firs(true); } else { @@ -456,9 +525,111 @@ double eiscat_radio_ctrl_impl::get_output_samp_rate(size_t /* port */) return EISCAT_RADIO_RATE; } +void eiscat_radio_ctrl_impl::set_rx_streamer(bool active, const size_t port) +{ + UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::set_rx_streamer() " << port << " -> " << active ; + if (port > EISCAT_NUM_PORTS) { + throw uhd::value_error(str( + boost::format("[%s] Can't (un)register RX streamer on port %d (invalid port)") + % unique_id() % port + )); + } + _rx_streamer_active[port] = active; + if (not check_radio_config()) { + throw std::runtime_error(str( + boost::format("[%s]: Invalid radio configuration.") + % unique_id() + )); + } + + if (list_upstream_nodes().empty() or not bool(get_arg<int>("use_prev"))) { + UHD_LOG_DEBUG(unique_id(), "No prevs found, or prevs disabled, not passing on set_rx_streamer"); + } else { + UHD_LOG_DEBUG(unique_id(), "set_rx_streamer(): We have prevs, so passing on set_rx_streamer"); + source_node_ctrl::sptr this_upstream_block_ctrl = + boost::dynamic_pointer_cast<source_node_ctrl>(list_upstream_nodes().at(0).lock()); + if (this_upstream_block_ctrl) { + this_upstream_block_ctrl->set_rx_streamer(active, port); + } else { + UHD_LOG_WARNING(unique_id(), "Oh noes, couldn't lock sptr!"); + } + } +} + +void eiscat_radio_ctrl_impl::issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd, const size_t chan) +{ + std::lock_guard<std::mutex> lock(_mutex); + + // Turn on/off top ones + if (list_upstream_nodes().empty() or not bool(get_arg<int>("use_prev"))) { + UHD_LOG_DEBUG(unique_id(), "No prevs found, or prevs disabled, not passing on stream cmd"); + } else { + UHD_LOG_DEBUG(unique_id(), "issue_stream_cmd(): We have prevs, so passing on stream command"); + source_node_ctrl::sptr this_upstream_block_ctrl = + boost::dynamic_pointer_cast<source_node_ctrl>(list_upstream_nodes().at(0).lock()); + if (this_upstream_block_ctrl) { + this_upstream_block_ctrl->issue_stream_cmd( + stream_cmd, + chan + ); + } else { + UHD_LOG_WARNING(unique_id(), "Oh noes, couldn't lock sptr!"); + } + } + + // Turn on/off this one + UHD_LOGGER_DEBUG(unique_id()) << "eiscat_radio_ctrl_impl::issue_stream_cmd() " << chan << " " << char(stream_cmd.stream_mode); + if (not _is_streamer_active(uhd::RX_DIRECTION, chan)) { + UHD_RFNOC_BLOCK_TRACE() << "radio_ctrl_impl::issue_stream_cmd() called on inactive channel. Skipping." ; + return; + } + UHD_ASSERT_THROW(stream_cmd.num_samps <= 0x0fffffff); + _continuous_streaming[chan] = (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + + if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS && + stream_cmd.stream_now == false) { + UHD_LOG_TRACE("EISCAT", "Stop cmd timed, setting cmd time!"); + set_command_time(stream_cmd.time_spec, chan); + } + + //setup the mode to instruction flags + typedef boost::tuple<bool, bool, bool, bool> inst_t; + static const uhd::dict<stream_cmd_t::stream_mode_t, inst_t> mode_to_inst = boost::assign::map_list_of + //reload, chain, samps, stop + (stream_cmd_t::STREAM_MODE_START_CONTINUOUS, inst_t(true, true, false, false)) + (stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS, inst_t(false, false, false, true)) + (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE, inst_t(false, false, true, false)) + (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE, inst_t(false, true, true, false)) + ; + + //setup the instruction flag values + bool inst_reload, inst_chain, inst_samps, inst_stop; + boost::tie(inst_reload, inst_chain, inst_samps, inst_stop) = mode_to_inst[stream_cmd.stream_mode]; + + //calculate the word from flags and length + uint32_t cmd_word = 0; + cmd_word |= uint32_t((stream_cmd.stream_now)? 1 : 0) << 31; + cmd_word |= uint32_t((inst_chain)? 1 : 0) << 30; + cmd_word |= uint32_t((inst_reload)? 1 : 0) << 29; + cmd_word |= uint32_t((inst_stop)? 1 : 0) << 28; + cmd_word |= (inst_samps)? stream_cmd.num_samps : ((inst_stop)? 0 : 1); + + //issue the stream command + const uint64_t ticks = (stream_cmd.stream_now)? 0 : stream_cmd.time_spec.to_ticks(get_rate()); + sr_write(regs::RX_CTRL_CMD, cmd_word, chan); + sr_write(regs::RX_CTRL_TIME_HI, uint32_t(ticks >> 32), chan); + sr_write(regs::RX_CTRL_TIME_LO, uint32_t(ticks >> 0), chan); //latches the command + UHD_LOG_INFO(unique_id(), "issued stream command."); + if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS && + stream_cmd.stream_now == false) { + UHD_LOG_TRACE("EISCAT", "Stop cmd timed, setting cmd time!"); + set_command_time(uhd::time_spec_t(0.0), chan); + } + +} + bool eiscat_radio_ctrl_impl::check_radio_config() { - UHD_RFNOC_BLOCK_TRACE() << "x300_radio_ctrl_impl::check_radio_config() " ; const uint32_t config_beams = get_arg<int>("configure_beams"); bool skipping_neighbours = config_beams & EISCAT_SKIP_NEIGHBOURS; bool upper_contrib = config_beams & EISCAT_CONTRIB_UPPER; @@ -511,18 +682,35 @@ void eiscat_radio_ctrl_impl::set_rpc_client( "EISCAT", "Finalizing dboard initialization; initializing JESD cores and ADCs." ); - if (not assert_jesd_cores_initialized()) { - throw uhd::runtime_error("Failed to initialize JESD cores and reset ADCs!"); - } - send_sysref(); - if (not assert_adcs_deframers()) { - throw uhd::runtime_error("Failed to initialize ADCs and JESD deframers!"); - } - send_sysref(); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - if (not assert_deframer_status()) { - throw uhd::runtime_error("Failed to finalize JESD core setup!"); + + /* Start of the ADC synchronization operation. + * These steps must be repeated if any ADC fails its deframer check + * Changing the sync line from SyncbAB to SyncnCD usually resolves the error + */ + const size_t possible_sync_combinations = 16; // 2 sync lines ^ (2 ADCs * 2 Daughtercards) + for (size_t iteration = 0; iteration < possible_sync_combinations; iteration++) { + UHD_LOG_INFO( + "EISCAT", + "looping to initialize JESD cores and ADCs." + ); + if (not assert_jesd_cores_initialized()) { + throw uhd::runtime_error("Failed to initialize JESD cores and reset ADCs!"); + } + send_sysref(); + + if (not assert_adcs_deframers()) { + throw uhd::runtime_error("Failed to initialize ADCs and JESD deframers!"); + } + send_sysref(); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + if (assert_deframer_status()) { + return; + } } + + // Unable to find a sync line combination which works + throw uhd::runtime_error("Failed to finalize JESD core setup!"); } /**************************************************************************** @@ -597,13 +785,13 @@ void eiscat_radio_ctrl_impl::select_filter( | (beam_index & 0xF) << 18 | send_now << 22 ; - if (not send_now) { UHD_LOG_TRACE("EISCAT", str( boost::format("Filter selection will be applied at " "time %f (0x%016X == %u). %s") % time_spec.get_full_secs() % time_spec.to_ticks(EISCAT_TICK_RATE) + % time_spec.to_ticks(EISCAT_TICK_RATE) % (write_time ? "Writing time regs now." : "Assuming time regs already up-to-date.") )); @@ -632,9 +820,9 @@ void eiscat_radio_ctrl_impl::set_antenna_gain( const size_t antenna_idx, const double normalized_gain ) { - if (normalized_gain < -1.0 or normalized_gain > 1.0) { + if (normalized_gain < -16.0 or normalized_gain > 16.0) { throw uhd::value_error(str( - boost::format("Invalid gain value for antenna %d: %f") + boost::format("Invalid digital gain value for antenna %d: %f") % antenna_idx % normalized_gain )); } @@ -643,7 +831,7 @@ void eiscat_radio_ctrl_impl::set_antenna_gain( EISCAT_MIN_GAIN, std::min( EISCAT_MAX_GAIN, - int32_t(normalized_gain * EISCAT_MAX_GAIN) + int32_t(normalized_gain * EISCAT_UNIT_GAIN) ) ); 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 959e5aa74..99d35c4a0 100644 --- a/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp +++ b/host/lib/usrp/dboard/eiscat/eiscat_radio_ctrl_impl.hpp @@ -151,6 +151,9 @@ public: //! \returns The EISCAT sampling rate double get_output_samp_rate(size_t port); + void set_rx_streamer(bool active, const size_t port); + void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd, const size_t port); + protected: virtual bool check_radio_config(); diff --git a/mpm/python/usrp_mpm/dboard_manager/eiscat.py b/mpm/python/usrp_mpm/dboard_manager/eiscat.py index 341af86b8..ee304d77d 100644 --- a/mpm/python/usrp_mpm/dboard_manager/eiscat.py +++ b/mpm/python/usrp_mpm/dboard_manager/eiscat.py @@ -71,7 +71,7 @@ class ADS54J56(object): else: self.sync_line = "AB" assert self.sync_line in ('AB', 'CD') - self.log.trace( + self.log.debug( "The next setup() sequence will use sync pin: {}".format( self.sync_line ) @@ -140,13 +140,11 @@ class ADS54J56(object): self.regs.poke8(0x7016, 0x02) # PLL mode 40x for C-D self.regs.poke8(0x4003, 0x00) # Select digital page in JESD Bank self.regs.poke8(0x4004, 0x69) # - self.regs.poke8(0x6000, 0x40) # Enable JESD Mode control for A-B + self.regs.poke8(0x6000, 0xC0) # Enable JESD Mode control & set K for A-B self.regs.poke8(0x6001, 0x02) # Set JESD Mode to 40x for LMFS=2441 - self.regs.poke8(0x7000, 0x40) # Enable JESD Mode control for C-D + self.regs.poke8(0x7000, 0xC0) # Enable JESD Mode control & set K for C-D self.regs.poke8(0x7001, 0x02) # Set JESD Mode to 40x for LMFS=2441 - self.regs.poke8(0x6000, 0x80) # Set CTRL K for A-B self.regs.poke8(0x6006, 0x0F) # Set K to 16 - self.regs.poke8(0x7000, 0x80) # Set CTRL K for C-D self.regs.poke8(0x7006, 0x0F) # Set K to 16 # Choose the sync pin. We have both connected up to the FPGA, but we # can only use one at a time. Sync pins can become non-functional (e.g. @@ -416,6 +414,15 @@ class EISCAT(DboardManagerBase): SYSREF_CONTROL = 0x0620 INIT_PHASE_DAC_WORD = 500 # Intentionally decimal + PHASE_DAC_SPI_ADDR = 0x3 + # External PPS pipeline delay from the PPS captured at the FPGA to TDC input, + # in reference clock ticks + EXT_PPS_DELAY = 3 + # Variable PPS delay before the RP/SP pulsers begin. Fixed value for the N3xx devices. + N3XX_INT_PPS_DELAY = 4 + default_master_clock_rate = 104e6 + default_time_source = 'external' + default_current_jesd_rate = 2500e6 def __init__(self, slot_idx, **kwargs): super(EISCAT, self).__init__(slot_idx, **kwargs) @@ -423,6 +430,7 @@ class EISCAT(DboardManagerBase): self.log.trace("Initializing EISCAT daughterboard, slot index {}".format(self.slot_idx)) self.initialized = False self.ref_clock_freq = 10e6 # This is the only supported clock rate + self.master_clock_rate = None # Define some attributes so that PyLint stays quiet: self.radio_regs = None self.jesd_cores = None @@ -497,20 +505,25 @@ class EISCAT(DboardManagerBase): init_phase_dac_word )) pdac_spi.poke16(0x3, init_phase_dac_word) - return LMK04828EISCAT(lmk_spi, ref_clk_freq, slot_idx) + return LMK04828EISCAT(lmk_spi, ref_clk_freq, slot_idx, self.log) def _sync_db_clock(): " Synchronizes the DB clock to the common reference " + reg_offset = 0x200 + ext_pps_delay = self.EXT_PPS_DELAY + #from outdated inst of ClockSync + #2.496e9, # lmk_vco_freq synchronizer = ClockSynchronizer( - self.dboard_clk_control, + self.radio_regs, self.lmk, self._spi_ifaces['phase_dac'], - 0, # register offset value. - 104e6, # TODO don't hardcode + reg_offset, + self.master_clock_rate, self.ref_clock_freq, 1.9E-12, # fine phase shift. TODO don't hardcode. This should live in the EEPROM self.INIT_PHASE_DAC_WORD, - 0x3, - 3, # External PPS pipeline delay from the PPS captured at the FPGA to TDC input + self.PHASE_DAC_SPI_ADDR, + ext_pps_delay, + self.N3XX_INT_PPS_DELAY, self.slot_idx) # The radio clock traces on the motherboard are 69 ps longer for Daughterboard B # than Daughterboard A. We want both of these clocks to align at the converters @@ -555,6 +568,8 @@ class EISCAT(DboardManagerBase): self.log.debug("Loaded SPI interfaces!") self._init_power(self.radio_regs) # Now, we can talk to chips via SPI self.dboard_clk_control = _init_clock_control(self.radio_regs) + self.ref_clock_freq = 10e6 # This is the only supported clock rate + self.master_clock_rate = self.default_master_clock_rate self.lmk = _init_lmk( self.slot_idx, self._spi_ifaces['lmk'], @@ -569,21 +584,8 @@ class EISCAT(DboardManagerBase): self.dboard_clk_control.enable_mmcm() self.log.debug("Clocking Configured Successfully!") # Synchronize DB Clocks - self.clock_synchronizer = ClockSynchronizer( - self.radio_regs, - self.dboard_clk_control, - self.lmk, - self._spi_ifaces['phase_dac'], - 0, # TODO this might not actually be zero - 104e6, # TODO don't hardcode - self.ref_clock_freq, - 1.9E-12, # TODO don't hardcode. This should live in the EEPROM - self.INIT_PHASE_DAC_WORD, - 2.496e9, # lmk_vco_freq - 0x3, # spi_addr - self.slot_idx - ) - _sync_db_clock(self.clock_synchronizer) + _sync_db_clock() + self.log.debug("Clocks Sync'd Successfully!") # Clocks and PPS are now fully active! return True @@ -659,7 +661,11 @@ class EISCAT(DboardManagerBase): "is fine." ) return True + + error = False + self.log.trace("check deframer status of both jesd cores.") for jesd_idx, jesd_core in enumerate(self.jesd_cores): + self.log.trace("check deframer status of jesd core {}.".format(jesd_idx)) if not jesd_core.check_deframer_status(): self.log.error("JESD204B Core {} Error: Failed to Link. " \ "Don't ignore this, please tell someone!".format(jesd_idx) diff --git a/mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py b/mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py index c2f35232d..509d65be0 100644 --- a/mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py +++ b/mpm/python/usrp_mpm/dboard_manager/lmk_eiscat.py @@ -14,8 +14,8 @@ class LMK04828EISCAT(LMK04828): """ LMK04828 controls for EISCAT daughterboard """ - def __init__(self, regs_iface, ref_clock_freq, slot=None): - LMK04828.__init__(self, regs_iface, slot) + def __init__(self, regs_iface, ref_clock_freq, slot=None, log=None): + LMK04828.__init__(self, regs_iface, log) self.log.trace("Using reference clock frequency {} MHz".format(ref_clock_freq/1e6)) if ref_clock_freq != 10e6: error_msg = "Invalid reference clock frequency: {} MHz. " \ @@ -26,6 +26,13 @@ class LMK04828EISCAT(LMK04828): self.init() self.config() + + def get_vco_freq(self): + """ + Return the hard coded VCO frequency in the LMK PLL2. + """ + return 2.496e9 + def init(self): """ Basic init. Turns it on. Let's us read SPI. |