diff options
Diffstat (limited to 'mpm/python/usrp_mpm/dboard_manager/mg_init.py')
-rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/mg_init.py | 567 |
1 files changed, 567 insertions, 0 deletions
diff --git a/mpm/python/usrp_mpm/dboard_manager/mg_init.py b/mpm/python/usrp_mpm/dboard_manager/mg_init.py new file mode 100644 index 000000000..34016e9f5 --- /dev/null +++ b/mpm/python/usrp_mpm/dboard_manager/mg_init.py @@ -0,0 +1,567 @@ +# +# Copyright 2018 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Helper class to initialize a Magnesium daughterboard +""" + +from __future__ import print_function +import re +import time +import math +from builtins import object + +from usrp_mpm.sys_utils.uio import open_uio +from usrp_mpm.dboard_manager.lmk_mg import LMK04828Mg +from usrp_mpm.dboard_manager.mg_periphs import DboardClockControl +from usrp_mpm.cores import ClockSynchronizer +from usrp_mpm.cores import nijesdcore +from usrp_mpm.mpmutils import async_exec + +INIT_CALIBRATION_TABLE = {"TX_BB_FILTER" : 0x0001, + "ADC_TUNER" : 0x0002, + "TIA_3DB_CORNER" : 0x0004, + "DC_OFFSET" : 0x0008, + "TX_ATTENUATION_DELAY" : 0x0010, + "RX_GAIN_DELAY" : 0x0020, + "FLASH_CAL" : 0x0040, + "PATH_DELAY" : 0x0080, + "TX_LO_LEAKAGE_INTERNAL" : 0x0100, + "TX_LO_LEAKAGE_EXTERNAL" : 0x0200, + "TX_QEC_INIT" : 0x0400, + "LOOPBACK_RX_LO_DELAY" : 0x0800, + "LOOPBACK_RX_RX_QEC_INIT" : 0x1000, + "RX_LO_DELAY" : 0x2000, + "RX_QEC_INIT" : 0x4000, + "BASIC" : 0x4F, + "OFF" : 0x00, + "DEFAULT" : 0x4DFF, + "ALL" : 0x7DFF, + } + +TRACKING_CALIBRATION_TABLE = {"TRACK_RX1_QEC" : 0x01, + "TRACK_RX2_QEC" : 0x02, + "TRACK_ORX1_QEC" : 0x04, + "TRACK_ORX2_QEC" : 0x08, + "TRACK_TX1_LOL" : 0x10, + "TRACK_TX2_LOL" : 0x20, + "TRACK_TX1_QEC" : 0x40, + "TRACK_TX2_QEC" : 0x80, + "OFF" : 0x00, + "RX_QEC" : 0x03, + "TX_QEC" : 0xC0, + "TX_LOL" : 0x30, + "DEFAULT" : 0xC3, + "ALL" : 0xF3, + } + + + +class MagnesiumInitManager(object): + """ + Helper class: Holds all the logic to initialize an N310/N300 (Magnesium) + daughterboard. + """ + # DAC is initialized to midscale automatically on power-on: 16-bit DAC, so + # midpoint is at 2^15 = 32768. However, the linearity of the DAC is best + # just below that point, so we set it to the (carefully calculated) + # alternate value instead. + INIT_PHASE_DAC_WORD = 31000 # Intentionally decimal + PHASE_DAC_SPI_ADDR = 0x0 + # External PPS pipeline delay from the PPS captured at the FPGA to TDC input, + # in reference clock ticks + EXT_PPS_DELAY = 5 + # Variable PPS delay before the RP/SP pulsers begin. Fixed value for the + # N3xx devices. + N3XX_INT_PPS_DELAY = 4 + + def __init__(self, mg_class, spi_ifaces): + self.mg_class = mg_class + self._spi_ifaces = spi_ifaces + self.mykonos = mg_class.mykonos + self.slot_idx = mg_class.slot_idx + self.log = mg_class.log.getChild('init') + + def check_mykonos_framer_status(self): + " Return True if Mykonos Framer is in good state " + rb = self.mykonos.get_framer_status() + self.log.trace("Mykonos Framer Status Register: 0x{:04X}".format(rb & 0xFF)) + tx_state = {0: 'CGS', + 1: 'ILAS', + 2: 'ADC Data'}[rb & 0b11] + ilas_state = {0: 'CGS', + 1: '1st Multframe', + 2: '2nd Multframe', + 3: '3rd Multframe', + 4: '4th Multframe', + 5: 'Last Multframe', + 6: 'invalid state', + 7: 'ILAS Complete'}[(rb & 0b11100) >> 2] + sysref_rx = (rb & (0b1 << 5)) > 0 + fifo_ptr_delta_changed = (rb & (0b1 << 6)) > 0 + sysref_phase_error = (rb & (0b1 << 7)) > 0 + # According to emails with ADI, fifo_ptr_delta_changed may be buggy. + # Deterministic latency is still achieved even when this bit is toggled, so + # ADI's recommendation is to ignore it. The expected state of this bit 0, but + # occasionally it toggles to 1. It is unclear why exactly this happens. + success = ((tx_state == 'ADC Data') & + (ilas_state == 'ILAS Complete') & + sysref_rx & + (not sysref_phase_error)) + logger = self.log.trace if success else self.log.warning + logger("Mykonos Framer, TX State: %s", tx_state) + logger("Mykonos Framer, ILAS State: %s", ilas_state) + logger("Mykonos Framer, SYSREF Received: {}".format(sysref_rx)) + logger("Mykonos Framer, FIFO Ptr Delta Change: {} (ignored, possibly buggy)" + .format(fifo_ptr_delta_changed)) + logger("Mykonos Framer, SYSREF Phase Error: {}" + .format(sysref_phase_error)) + return success + + + def check_mykonos_deframer_status(self): + " Return True if Mykonos Deframer is in good state " + rb = self.mykonos.get_deframer_status() + self.log.trace("Mykonos Deframer Status Register: 0x{:04X}".format(rb & 0xFF)) + + frame_symbol_error = (rb & (0b1 << 0)) > 0 + ilas_multifrm_error = (rb & (0b1 << 1)) > 0 + ilas_framing_error = (rb & (0b1 << 2)) > 0 + ilas_checksum_valid = (rb & (0b1 << 3)) > 0 + prbs_error = (rb & (0b1 << 4)) > 0 + sysref_received = (rb & (0b1 << 5)) > 0 + deframer_irq = (rb & (0b1 << 6)) > 0 + success = ((not frame_symbol_error) & + (not ilas_multifrm_error) & + (not ilas_framing_error) & + ilas_checksum_valid & + (not prbs_error) & + sysref_received & + (not deframer_irq)) + logger = self.log.trace if success else self.log.warning + logger("Mykonos Deframer, Frame Symbol Error: {}".format(frame_symbol_error)) + logger("Mykonos Deframer, ILAS Multiframe Error: {}".format(ilas_multifrm_error)) + logger("Mykonos Deframer, ILAS Frame Error: {}".format(ilas_framing_error)) + logger("Mykonos Deframer, ILAS Checksum Valid: {}".format(ilas_checksum_valid)) + logger("Mykonos Deframer, PRBS Error: {}".format(prbs_error)) + logger("Mykonos Deframer, SYSREF Received: {}".format(sysref_received)) + logger("Mykonos Deframer, Deframer IRQ Received: {}".format(deframer_irq)) + return success + + + def _init_lmk( + self, + lmk_spi, + ref_clock_freq, + master_clock_rate, + pdac_spi, + init_phase_dac_word, + phase_dac_spi_addr): + """ + Sets the phase DAC to initial value, and then brings up the LMK + according to the selected ref clock frequency. + Will throw if something fails. + """ + self.log.trace("Initializing Phase DAC to d{}.".format( + init_phase_dac_word + )) + pdac_spi.poke16(phase_dac_spi_addr, init_phase_dac_word) + return LMK04828Mg( + lmk_spi, + self.mg_class.spi_lock, + ref_clock_freq, + master_clock_rate, + self.log + ) + + + def _sync_db_clock( + self, + dboard_ctrl_regs, + master_clock_rate, + ref_clock_freq, + args): + """ Synchronizes the DB clock to the common reference """ + reg_offset = 0x200 + ext_pps_delay = self.EXT_PPS_DELAY + if args.get('time_source', self.mg_class.default_time_source) == 'sfp0': + reg_offset = 0x400 + ref_clock_freq = 62.5e6 + ext_pps_delay = 1 # only 1 flop between the WR core output and the TDC input + synchronizer = ClockSynchronizer( + dboard_ctrl_regs, + self.mg_class.lmk, + self._spi_ifaces['phase_dac'], + reg_offset, + master_clock_rate, + ref_clock_freq, + 860E-15, # fine phase shift. TODO don't hardcode. This should live in the EEPROM + self.INIT_PHASE_DAC_WORD, + 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 on each board, so adjust the target value for + # DB B. This is an N3xx series peculiarity and will not apply to other + # motherboards. + trace_delay_offset = {0: 0.0e-0, + 1: 69.0e-12}[self.slot_idx] + offset = synchronizer.run( + num_meas=[512, 128], + target_offset=trace_delay_offset) + offset_error = abs(offset) + if offset_error > 100e-12: + self.log.error("Clock synchronizer measured an offset of {:.1f} ps!".format( + offset_error*1e12 + )) + raise RuntimeError("Clock synchronizer measured an offset of {:.1f} ps!".format( + offset_error*1e12 + )) + else: + self.log.debug("Residual synchronization error: {:.1f} ps.".format( + offset_error*1e12 + )) + synchronizer = None # Help garbage collector + self.log.debug("Sample Clock Synchronization Complete!") + + + def set_jesd_rate(self, jesdcore, new_rate, current_jesd_rate, force=False): + """ + Make the QPLL and GTX changes required to change the JESD204B core rate. + """ + # The core is directly compiled for 125 MHz sample rate, which + # corresponds to a lane rate of 2.5 Gbps. The same QPLL and GTX settings + # apply for the 122.88 MHz sample rate. + # + # The higher LTE rate, 153.6 MHz, requires changes to the default + # configuration of the MGT components. This function performs the + # required changes in the following order (as recommended by UG476). + # + # 1) Modify any QPLL settings. + # 2) Perform the QPLL reset routine by pulsing reset then waiting for lock. + # 3) Modify any GTX settings. + # 4) Perform the GTX reset routine by pulsing reset and waiting for reset done. + assert new_rate in (2457.6e6, 2500e6, 3072e6) + + # On first run, we have no idea how the FPGA is configured... so let's force an + # update to our rate. + force = force or current_jesd_rate is None + + skip_drp = False + if not force: + # Current New Skip? + skip_drp = {2457.6e6 : {2457.6e6: True, 2500.0e6: True, 3072.0e6:False}, + 2500.0e6 : {2457.6e6: True, 2500.0e6: True, 3072.0e6:False}, + 3072.0e6 : {2457.6e6: False, 2500.0e6: False, 3072.0e6:True}}[self.mg_class.current_jesd_rate][new_rate] + if skip_drp: + self.log.trace( + "Current lane rate is compatible with the new rate. " + "Skipping reconfiguration.") + + # These are the only registers in the QPLL and GTX that change based on the + # selected line rate. The MGT wizard IP was generated for each of the rates and + # reference clock frequencies and then diffed to create this table. + QPLL_CFG = {2457.6e6: 0x680181, 2500e6: 0x680181, 3072e6: 0x06801C1}[new_rate] + QPLL_FBDIV = {2457.6e6: 0x120, 2500e6: 0x120, 3072e6: 0x80}[new_rate] + MGT_PMA_RSV = {2457.6e6: 0x1E7080, 2500e6: 0x1E7080, 3072e6: 0x18480}[new_rate] + MGT_RX_CLK25_DIV = {2457.6e6: 5, 2500e6: 5, 3072e6: 7}[new_rate] + MGT_TX_CLK25_DIV = {2457.6e6: 5, 2500e6: 5, 3072e6: 7}[new_rate] + MGT_RXOUT_DIV = {2457.6e6: 4, 2500e6: 4, 3072e6: 2}[new_rate] + MGT_TXOUT_DIV = {2457.6e6: 4, 2500e6: 4, 3072e6: 2}[new_rate] + MGT_RXCDR_CFG = {2457.6e6:0x03000023ff10100020, 2500e6:0x03000023ff10100020, 3072e6:0x03000023ff10200020}[new_rate] + + # 1-2) Do the QPLL first + if not skip_drp: + self.log.trace("Changing QPLL settings to support {} Gbps".format(new_rate/1e9)) + jesdcore.set_drp_target('qpll', 0) + # QPLL_CONFIG is spread across two regs: 0x32 (dedicated) and 0x33 (shared) + reg_x32 = QPLL_CFG & 0xFFFF # [16:0] -> [16:0] + reg_x33 = jesdcore.drp_access(rd=True, addr=0x33) + reg_x33 = (reg_x33 & 0xF800) | ((QPLL_CFG >> 16) & 0x7FF) # [26:16] -> [11:0] + jesdcore.drp_access(rd=False, addr=0x32, wr_data=reg_x32) + jesdcore.drp_access(rd=False, addr=0x33, wr_data=reg_x33) + # QPLL_FBDIV is shared with other settings in reg 0x36 + reg_x36 = jesdcore.drp_access(rd=True, addr=0x36) + reg_x36 = (reg_x36 & 0xFC00) | (QPLL_FBDIV & 0x3FF) # in bits [9:0] + jesdcore.drp_access(rd=False, addr=0x36, wr_data=reg_x36) + + # Run the QPLL reset sequence and prep the MGTs for modification. + jesdcore.init() + + # 3-4) And the 4 MGTs second + if not skip_drp: + self.log.trace("Changing MGT settings to support {} Gbps" + .format(new_rate/1e9)) + for lane in range(4): + jesdcore.set_drp_target('mgt', lane) + # MGT_PMA_RSV is split over 0x99 (LSBs) and 0x9A + reg_x99 = MGT_PMA_RSV & 0xFFFF + reg_x9a = (MGT_PMA_RSV >> 16) & 0xFFFF + jesdcore.drp_access(rd=False, addr=0x99, wr_data=reg_x99) + jesdcore.drp_access(rd=False, addr=0x9A, wr_data=reg_x9a) + # MGT_RX_CLK25_DIV is embedded with others in 0x11. The + # encoding for the DRP register value is one less than the + # desired value. + reg_x11 = jesdcore.drp_access(rd=True, addr=0x11) + reg_x11 = (reg_x11 & 0xF83F) | \ + ((MGT_RX_CLK25_DIV-1 & 0x1F) << 6) # [10:6] + jesdcore.drp_access(rd=False, addr=0x11, wr_data=reg_x11) + # MGT_TX_CLK25_DIV is embedded with others in 0x6A. The + # encoding for the DRP register value is one less than the + # desired value. + reg_x6a = jesdcore.drp_access(rd=True, addr=0x6A) + reg_x6a = (reg_x6a & 0xFFE0) | (MGT_TX_CLK25_DIV-1 & 0x1F) # [4:0] + jesdcore.drp_access(rd=False, addr=0x6A, wr_data=reg_x6a) + # MGT_RXCDR_CFG is split over 0xA8 (LSBs) through 0xAD + for reg_num, reg_addr in enumerate(range(0xA8, 0xAE)): + reg_data = (MGT_RXCDR_CFG >> 16*reg_num) & 0xFFFF + jesdcore.drp_access(rd=False, addr=reg_addr, wr_data=reg_data) + # MGT_RXOUT_DIV and MGT_TXOUT_DIV are embedded together in + # 0x88. The encoding for the DRP register value is + # drp_val=log2(attribute) + reg_x88 = (int(math.log(MGT_RXOUT_DIV, 2)) & 0x7) | \ + ((int(math.log(MGT_TXOUT_DIV, 2)) & 0x7) << 4) # RX=[2:0] TX=[6:4] + jesdcore.drp_access(rd=False, addr=0x88, wr_data=reg_x88) + self.log.trace("GTX settings changed to support {} Gbps" + .format(new_rate/1e9)) + jesdcore.disable_drp_target() + self.log.trace("JESD204b Lane Rate set to {} Gbps!" + .format(new_rate/1e9)) + self.mg_class.current_jesd_rate = new_rate + return + + + def init_lo_source(self, args): + """Configure LO sources + + This function will initialize all LO sources to user specified sources. + If there's no source is specified, the default one will be used. + + Arguments: + args {string:string} -- device arguments. + """ + self.log.debug("Setting up LO source..") + rx_lo_source = args.get("rx_lo_source", "internal") + tx_lo_source = args.get("tx_lo_source", "internal") + self.mykonos.set_lo_source("RX", rx_lo_source) + self.mykonos.set_lo_source("TX", tx_lo_source) + self.log.debug("RX LO source is set at {}".format(self.mykonos.get_lo_source("RX"))) + self.log.debug("TX LO source is set at {}".format(self.mykonos.get_lo_source("TX"))) + + + def init_rf_cal(self, args): + """ Setup RF CAL """ + def _parse_and_convert_cal_args(table, cal_args): + """Parse calibration string and convert it to a number + + Arguments: + table {dictionary} -- a look up table that map a type of calibration + to its bit mask.(defined in AD9375-UG992) + cal_args {string} -- string arguments from user in form of "CAL1|CAL2|CAL3" + or "CAL1 CAL2 CAL3" or "CAL1;CAL2;CAL3" + + Returns: + int -- calibration value bit mask. + """ + value = 0 + try: + return int(cal_args, 0) + except ValueError: + pass + for key in re.split(r'[;|\s]\s*', cal_args): + value_tmp = table.get(key.upper()) + if (value_tmp) != None: + value |= value_tmp + else: + self.log.warning( + "Calibration key `%s' is not in calibration table. " + "Ignoring this key.", + key.upper() + ) + return value + ## Go, go, go! + self.log.trace("Setting up RF CAL...") + try: + init_cals_mask = _parse_and_convert_cal_args( + INIT_CALIBRATION_TABLE, + args.get('init_cals', 'DEFAULT') + ) + tracking_cals_mask = _parse_and_convert_cal_args( + TRACKING_CALIBRATION_TABLE, + args.get('tracking_cals', 'DEFAULT') + ) + init_cals_timeout = int( + args.get( + 'init_cals_timeout', + str(self.mykonos.DEFAULT_INIT_CALS_TIMEOUT) + ), 0 + ) + except ValueError as ex: + self.log.warning("init() args missing or error using default \ + value seeing following exception print out.") + self.log.warning("{}".format(ex)) + init_cals_mask = _parse_and_convert_cal_args( + INIT_CALIBRATION_TABLE, 'DEFAULT') + tracking_cals_mask = _parse_and_convert_cal_args( + TRACKING_CALIBRATION_TABLE, 'DEFAULT') + init_cals_timeout = self.mykonos.DEFAULT_INIT_CALS_TIMEOUT + self.log.debug("args[init_cals]=0x{:02X}".format(init_cals_mask)) + self.log.debug("args[tracking_cals]=0x{:02X}".format(tracking_cals_mask)) + async_exec( + self.mykonos, + "setup_cal", + init_cals_mask, + tracking_cals_mask, + init_cals_timeout + ) + + + def init_jesd(self, jesdcore, master_clock_rate, args): + """ + Bring up the JESD link between Mykonos and the N310. + All clocks must be set up and stable before starting this routine. + """ + jesdcore.check_core() + + # JESD Lane Rate only depends on the master_clock_rate selection, since all + # other link parameters (LMFS,N) remain constant. + L = 4 + M = 4 + F = 2 + S = 1 + N = 16 + new_rate = master_clock_rate * M * N * (10.0/8) / L / S + self.log.trace("Calculated JESD204b lane rate is {} Gbps".format(new_rate/1e9)) + self.mg_class.current_jesd_rate = \ + self.set_jesd_rate( + jesdcore, + new_rate, + self.mg_class.current_jesd_rate) + self.log.trace("Pulsing Mykonos Hard Reset...") + self.mg_class.cpld.reset_mykonos() + self.log.trace("Initializing Mykonos...") + self.init_lo_source(args) + self.mykonos.begin_initialization() + # Multi-chip Sync requires two SYSREF pulses at least 17us apart. + jesdcore.send_sysref_pulse() + time.sleep(0.001) # 17us... ish. + jesdcore.send_sysref_pulse() + async_exec(self.mykonos, "finish_initialization") + # TODO:can we call this after JESD? + self.init_rf_cal(args) + self.log.trace("Starting JESD204b Link Initialization...") + # Generally, enable the source before the sink. Start with the DAC side. + self.log.trace("Starting FPGA framer...") + jesdcore.init_framer() + self.log.trace("Starting Mykonos deframer...") + self.mykonos.start_jesd_rx() + # Now for the ADC link. Note that the Mykonos framer will not start issuing CGS + # characters until SYSREF is received by the framer. Therefore we enable the + # framer in Mykonos and the FPGA, send a SYSREF pulse to everyone, and then + # start the deframer in the FPGA. + self.log.trace("Starting Mykonos framer...") + self.mykonos.start_jesd_tx() + jesdcore.enable_lmfc(True) + jesdcore.send_sysref_pulse() + # Allow a bit of time for SYSREF to reach Mykonos and then CGS to + # appear. In several experiments this time requirement was only in the + # 100s of nanoseconds. + time.sleep(0.001) + self.log.trace("Starting FPGA deframer...") + jesdcore.init_deframer() + + # Allow a bit of time for CGS/ILA to complete. + time.sleep(0.100) + error_flag = False + if not jesdcore.get_framer_status(): + self.log.error("JESD204b FPGA Core Framer is not synced!") + error_flag = True + if not self.check_mykonos_deframer_status(): + self.log.error("Mykonos JESD204b Deframer is not synced!") + error_flag = True + if not jesdcore.get_deframer_status(): + self.log.error("JESD204b FPGA Core Deframer is not synced!") + error_flag = True + if not self.check_mykonos_framer_status(): + self.log.error("Mykonos JESD204b Framer is not synced!") + error_flag = True + if (self.mykonos.get_multichip_sync_status() & 0xB) != 0xB: + self.log.error("Mykonos Multi-chip Sync failed!") + error_flag = True + if error_flag: + raise RuntimeError('JESD204B Link Initialization Failed. See MPM logs for details.') + self.log.debug("JESD204B Link Initialization & Training Complete") + + + def _full_init(self, slot_idx, master_clock_rate, ref_clock_freq, args): + """ + Run the full initialization sequence. This will bring everything up + from scratch: The LMK, JESD cores, the AD9371, calibrations, and + anything else that is clocking-related. + Depending on the settings, this can take a fair amount of time. + """ + # Init some more periphs: + # The following peripherals are only used during init, so we don't + # want to hang on to them for the full lifetime of the Magnesium + # class. This helps us close file descriptors associated with the + # UIO objects. + with open_uio( + label="dboard-regs-{}".format(slot_idx), + read_only=False + ) as dboard_ctrl_regs: + self.log.trace("Creating jesdcore object...") + jesdcore = nijesdcore.NIMgJESDCore(dboard_ctrl_regs, slot_idx) + # Now get cracking with the actual init sequence: + self.log.trace("Creating dboard clock control object...") + db_clk_control = DboardClockControl(dboard_ctrl_regs, self.log) + self.log.debug("Reset Dboard Clocking and JESD204B interfaces...") + db_clk_control.reset_mmcm() + jesdcore.reset() + self.log.trace("Initializing LMK...") + self.mg_class.lmk = self._init_lmk( + self._spi_ifaces['lmk'], + ref_clock_freq, + master_clock_rate, + self._spi_ifaces['phase_dac'], + self.INIT_PHASE_DAC_WORD, + self.PHASE_DAC_SPI_ADDR, + ) + db_clk_control.enable_mmcm() + # Synchronize DB Clocks + self._sync_db_clock( + dboard_ctrl_regs, + master_clock_rate, + ref_clock_freq, + args) + self.log.debug( + "Sample Clocks and Phase DAC Configured Successfully!") + # Clocks and PPS are now fully active! + self.mykonos.set_master_clock_rate(master_clock_rate) + self.init_jesd(jesdcore, master_clock_rate, args) + jesdcore = None # Help with garbage collection + # That's all that requires access to the dboard regs! + if bool(args.get('rfic_digital_loopback')): + self.log.warning( + "RF Functionality Disabled: JESD204b digital loopback " + "enabled inside Mykonos!") + self.mykonos.enable_jesd_loopback(1) + else: + self.mykonos.start_radio() + return True + + def init(self, args, fast_reinit): + """ + Runs the actual initialization. + """ + if not fast_reinit or True: + return self._full_init( + self.mg_class.slot_idx, + self.mg_class.master_clock_rate, + self.mg_class.ref_clock_freq, + args + ) |