diff options
| author | Martin Braun <martin.braun@ettus.com> | 2017-11-15 14:52:12 -0800 | 
|---|---|---|
| committer | Martin Braun <martin.braun@ettus.com> | 2017-12-22 15:05:07 -0800 | 
| commit | f14b49ff5b9c55972966c74b014850ecc89e75e2 (patch) | |
| tree | 6d98fc58ae9e4184c4936765d45be4d2ae7dce6b /mpm/python/usrp_mpm/dboard_manager/lmk_mg.py | |
| parent | 63f4049a101d66c01f7e89098b03f3f780647cbd (diff) | |
| download | uhd-f14b49ff5b9c55972966c74b014850ecc89e75e2.tar.gz uhd-f14b49ff5b9c55972966c74b014850ecc89e75e2.tar.bz2 uhd-f14b49ff5b9c55972966c74b014850ecc89e75e2.zip  | |
n3xx: add support for 122.88 and 153.6 MHz sample clock rates
 - re-wrote portions of the LMK driver for flexible rates and configuration
 - tweaked TDC driver for compatibility and ease of debugging
 - updated comments and log statements throughout for uniformity
Diffstat (limited to 'mpm/python/usrp_mpm/dboard_manager/lmk_mg.py')
| -rw-r--r-- | mpm/python/usrp_mpm/dboard_manager/lmk_mg.py | 123 | 
1 files changed, 76 insertions, 47 deletions
diff --git a/mpm/python/usrp_mpm/dboard_manager/lmk_mg.py b/mpm/python/usrp_mpm/dboard_manager/lmk_mg.py index e6a1821af..dc3fe203b 100644 --- a/mpm/python/usrp_mpm/dboard_manager/lmk_mg.py +++ b/mpm/python/usrp_mpm/dboard_manager/lmk_mg.py @@ -19,27 +19,60 @@ LMK04828 driver for use with Magnesium  """  import time +import math  from builtins import zip  from builtins import hex  from ..mpmlog import get_logger  from ..chips import LMK04828  class LMK04828Mg(LMK04828): -    def __init__(self, regs_iface, spi_lock, ref_clock_freq, log=None): +    def __init__(self, regs_iface, spi_lock, ref_clock_freq, master_clock_freq, log=None):          LMK04828.__init__(self, regs_iface, log) -        self.log.trace("Using reference clock frequency {} MHz".format(ref_clock_freq/1e6)) +        self.log.trace("Using reference clock frequency: {} MHz".format(ref_clock_freq/1e6)) +        self.log.trace("Using master clock frequency: {} MHz".format(master_clock_freq/1e6))          self.spi_lock = spi_lock          assert hasattr(self.spi_lock, 'lock')          assert hasattr(self.spi_lock, 'unlock')          self.ref_clock_freq = ref_clock_freq +        self.master_clock_freq = master_clock_freq +        # VCXO on Mg runs at 96 MHz +        self.vcxo_freq = 96e6 +        # Feedback clock divider is constant for Mg regardless of the master_clock_freq. +        self.clkfb_divider = 24 +        # PLL2 R value is also constant across all sample clock combinations. +        self.pll2_r_divider = 4 +        # PLL1 PFD is 1 MHz. Divide the ref_clock_freq by 1 MHz to set the R divider. +        self.clkin_r_divider = int(math.floor(self.ref_clock_freq/1e6)) +        self.clkout_divider = {122.88e6:   25, 125e6:   24, 153.6e6:   20}[self.master_clock_freq] +        self.pll1_n_divider = {122.88e6:  128, 125e6:  125, 153.6e6:  128}[self.master_clock_freq] +        self.sysref_divider = {122.88e6:  500, 125e6:  480, 153.6e6:  400}[self.master_clock_freq] +        self.pll2_prescaler = {122.88e6:    2, 125e6:    5, 153.6e6:    2}[self.master_clock_freq] +        self.pll2_n_divider = {122.88e6:   64, 125e6:   25, 153.6e6:   64}[self.master_clock_freq] +        self.pll2_vco_freq  = (self.vcxo_freq/self.pll2_r_divider)*self.pll2_prescaler*self.pll2_n_divider +        self.log.trace("Variable Configuration Report: " +                       "clkin1_r = 0d{}, clkout_div = 0d{}, pll1_n = 0d{}" +           .format(self.clkin_r_divider, self.clkout_divider, self.pll1_n_divider)) +        self.log.trace("Variable Configuration Report: " +                       "sysref_divider = 0d{}, pll2_pre = 0d{}, pll2_n = 0d{}" +           .format(self.sysref_divider, self.pll2_prescaler, self.pll2_n_divider)) +        self.log.trace("Variable Configuration Report: " +                       "pll2_vco_freq = 0d{}" +           .format(self.pll2_vco_freq)) +        # Run .init() and .config() right off the bat. Save clock shifty-ness for later.          self.init()          self.config() +    def get_vco_freq(self): +        """ +        Return the calculated VCO frequency in the LMK PLL2. +        """ +        return self.pll2_vco_freq +      def init(self):          """          Basic init. Turns it on. Let's read SPI.          """ -        self.log.info("Reset LMK & Verify") +        self.log.info("Reset and Verify Chip ID")          self.pokes8((              (0x000, 0x90), # Assert reset              (0x000, 0x10), # De-assert reset @@ -47,66 +80,67 @@ class LMK04828Mg(LMK04828):              (0x148, 0x33), # Clock Select as SDO          ))          if not self.verify_chip_id(): -            raise Exception("Unable to locate LMK04828") - +            raise Exception("Unable to locate LMK04828!")      def config(self):          """          Write lots of config foo.          """ -        self.log.trace("LMK Initialization") -        clkin0_r_divider = {10e6: 0x0A, 20e6: 0x14}[self.ref_clock_freq] +        clkout_div_val = self.divide_to_reg(self.clkout_divider) +        clkout_cnt_val = self.divide_to_cnth_cntl_reg(self.clkout_divider) + +        self.log.info("Register Initialization Commencing...")          self.pokes8(( -            (0x100, 0x78), # CLKout Config -            (0x101, 0xCC), # CLKout Config +            (0x100, clkout_div_val), # CLKout Config +            (0x101, clkout_cnt_val), # CLKout Config              (0x102, 0xCC), # CLKout Config              (0x103, 0x00), # CLKout Config              (0x104, 0x20), # CLKout Config              (0x105, 0x00), # CLKout Config              (0x106, 0x72), # CLKout Config MYK: (0xAB where A = SYSREF, B = CLK)              (0x107, 0x15), # CLKout Config 0x15 = LVDS, 0x55 = LVPECL -            (0x108, 0x7E), # CLKout Config -            (0x109, 0xFF), # CLKout Config +            (0x108, clkout_div_val), # CLKout Config +            (0x109, clkout_cnt_val), # CLKout Config              (0x10A, 0xFF), # CLKout Config              (0x10B, 0x00), # CLKout Config              (0x10C, 0x00), # CLKout Config              (0x10D, 0x00), # CLKout Config              (0x10E, 0x70), # CLKout Config              (0x10F, 0x55), # CLKout Config -            (0x110, 0x78), # CLKout Config -            (0x111, 0xCC), # CLKout Config +            (0x110, clkout_div_val), # CLKout Config +            (0x111, clkout_cnt_val), # CLKout Config              (0x112, 0xCC), # CLKout Config              (0x113, 0x00), # CLKout Config              (0x114, 0x00), # CLKout Config              (0x115, 0x00), # CLKout Config              (0x116, 0xF9), # CLKout Config              (0x117, 0x00), # CLKout Config -            (0x118, 0x78), # CLKout Config -            (0x119, 0xCC), # CLKout Config +            (0x118, self.divide_to_reg(self.clkfb_divider)), # CLKout Config +            (0x119, self.divide_to_cnth_cntl_reg(self.clkfb_divider)), # CLKout Config              (0x11A, 0xCC), # CLKout Config              (0x11B, 0x00), # CLKout Config              (0x11C, 0x20), # CLKout Config              (0x11D, 0x00), # CLKout Config              (0x11E, 0xF1), # CLKout Config              (0x11F, 0x00), # CLKout Config -            (0x120, 0x78), # CLKout Config -            (0x121, 0xCC), # CLKout Config +            (0x120, clkout_div_val), # CLKout Config +            (0x121, clkout_cnt_val), # CLKout Config              (0x122, 0xCC), # CLKout Config              (0x123, 0x00), # CLKout Config              (0x124, 0x20), # CLKout Config 0x20 = SYSREF output, 0x00 = DEVCLK              (0x125, 0x00), # CLKout Config              (0x126, 0x72), # CLKout Config FPGA: (0xAB where A = SYSREF, B = CLK)              (0x127, 0x55), # CLKout Config 0x1 = LVDS, 0x5 = LVPECL -            (0x128, 0x78), # CLKout Config -            (0x129, 0xCC), # CLKout Config +            (0x128, clkout_div_val), # CLKout Config +            (0x129, clkout_cnt_val), # CLKout Config              (0x12A, 0xCC), # CLKout Config              (0x12B, 0x00), # CLKout Config              (0x12C, 0x00), # CLKout Config              (0x12D, 0x00), # CLKout Config              (0x12E, 0x72), # CLKout Config              (0x12F, 0xD0), # CLKout Config -            (0x130, 0x78), # CLKout Config -            (0x131, 0xCC), # CLKout Config +            (0x130, clkout_div_val), # CLKout Config +            (0x131, clkout_cnt_val), # CLKout Config              (0x132, 0xCC), # CLKout Config              (0x133, 0x00), # CLKout Config              (0x134, 0x20), # CLKout Config @@ -115,8 +149,8 @@ class LMK04828Mg(LMK04828):              (0x137, 0x05), # CLKout Config              (0x138, 0x30), # VCO_MUX to VCO 1; OSCout off              (0x139, 0x00), # SYSREF Source = MUX; SYSREF MUX = Normal SYNC -            (0x13A, 0x01), # SYSREF Divide [12:8] -            (0x13B, 0xE0), # SYSREF Divide [7:0] +            (0x13A, (self.sysref_divider & 0x1F00) >> 8), # SYSREF Divide [12:8] +            (0x13B, (self.sysref_divider & 0x00FF) >> 0), # SYSREF Divide [7:0]              (0x13C, 0x00), # SYSREF DDLY [12:8]              (0x13D, 0x08), # SYSREF DDLY [7:0] ... 8 is default, <8 is reserved              (0x13E, 0x00), # SYSREF Pulse Count = 1 pulse/request @@ -141,13 +175,13 @@ class LMK04828Mg(LMK04828):              (0x151, 0x02), # Holdover Settings (defaults)              (0x152, 0x00), # Holdover Settings (defaults)              (0x153, 0x00), # CLKin0_R divider [13:8], default = 0 -            (0x154, clkin0_r_divider), # CLKin0_R divider [7:0], default = d120 -            (0x155, 0x00), # CLKin1_R divider [13:8], default = 0 -            (0x156, clkin0_r_divider), # CLKin1_R divider [7:0], default = d120 +            (0x154, 0x78), # CLKin0_R divider [7:0], default = d120 +            (0x155, (self.clkin_r_divider & 0x3F00) >> 8), # CLKin1_R divider [13:8], default = 0 +            (0x156, (self.clkin_r_divider & 0x00FF) >> 0), # CLKin1_R divider [7:0], default = d120              (0x157, 0x00), # CLKin2_R divider [13:8], default = 0              (0x158, 0x01), # CLKin2_R divider [7:0], default = d120 -            (0x159, 0x00), # PLL1 N divider [13:8], default = 0 -            (0x15A, 0x7D), # PLL1 N divider [7:0], default = d120 +            (0x159, (self.pll1_n_divider & 0x3F00) >> 8), # PLL1 N divider [13:8], default = 0 +            (0x15A, (self.pll1_n_divider & 0x00FF) >> 0), # PLL1 N divider [7:0], default = d120              (0x15B, 0xCF), # PLL1 PFD              (0x15C, 0x27), # PLL1 DLD Count [13:8]              (0x15D, 0x10), # PLL1 DLD Count [7:0] @@ -155,7 +189,7 @@ class LMK04828Mg(LMK04828):              (0x15F, 0x0B), # Status LD1 pin = PLL1 LD, push-pull output              (0x160, 0x00), # PLL2 R divider [11:8];              (0x161, 0x04), # PLL2 R divider [7:0] -            (0x162, 0xA4), # PLL2 prescaler; OSCin freq +            (0x162, self.pll2_pre_to_reg(self.pll2_prescaler)), # PLL2 prescaler; OSCin freq 0xA4              (0x163, 0x00), # PLL2 Cal = PLL2 normal val              (0x164, 0x00), # PLL2 Cal = PLL2 normal val              (0x165, 0x19), # PLL2 Cal = PLL2 normal val @@ -163,9 +197,9 @@ class LMK04828Mg(LMK04828):              (0x172, 0x02), # Write this val after x165              (0x17C, 0x15), # VCo1 Cal; write before x168              (0x17D, 0x33), # VCo1 Cal; write before x168 -            (0x166, 0x00), # PLL2 N[17:16] -            (0x167, 0x00), # PLL2 N[15:8] -            (0x168, 0x19), # PLL2 N[7:0] +            (0x166, (self.pll2_n_divider & 0x030000) >> 16), # PLL2 N[17:16] +            (0x167, (self.pll2_n_divider & 0x00FF00) >> 8), # PLL2 N[15:8] +            (0x168, (self.pll2_n_divider & 0x0000FF) >> 0), # PLL2 N[7:0]              (0x169, 0x51), # PLL2 PFD              (0x16A, 0x27), # PLL2 DLD Count [13:8] = default d32              (0x16B, 0x10), # PLL2 DLD Count [7:0] = default d0 @@ -187,16 +221,14 @@ class LMK04828Mg(LMK04828):                  (0x183, 0x1), # Clear Lock Detect Sticky                  (0x183, 0x0), # Clear Lock Detect Sticky              )) -            # Wait a bit before checking for lock -            # time.sleep(0.050)              if self.check_plls_locked():                  locked = True -                self.log.info("LMK PLLs Locked!") +                self.log.trace("PLLs are Locked!")                  break          if not locked: -            raise RuntimeError("At least one LMK PLL did not lock! Check the logs for details.") +            raise RuntimeError("At least one PLL did not lock! Check the logs for details.") -        self.log.trace("Setting SYNC and SYSREF config...") +        self.log.trace("Synchronizing output dividers...")          self.pokes8((              (0x143, 0xF1), # toggle SYNC polarity to trigger SYNC event              (0x143, 0xD1), # toggle SYNC polarity to trigger SYNC event @@ -204,17 +236,15 @@ class LMK04828Mg(LMK04828):              (0x144, 0xFF), # Disable SYNC on all outputs including sysref              (0x143, 0x52), # Pulser selected; SYNC enabled; 1 shot enabled          )) -        self.log.info("LMK init'd and locked!") +        self.log.info("Clocks Initialized and PLLs Locked!")      def lmk_shift(self, num_shifts=0):          """ -        Apply time shift +        Apply time shift using the dynamic digital delays inside the LMK.          """ -        # TODO: these numbers need to be based off the radio clock freq. -        self.log.trace("LMK04828 Clock Phase Shifting Commencing...") -        ddly_value = 0xCD if num_shifts >= 0 else 0xCB -        ddly_value_sysref_reg0 = 0x01 -        ddly_value_sysref_reg1 = 0xE1 if num_shifts >= 0 else 0xDF # 0xE0 is normal +        self.log.trace("Clock Shifting Commencing using Dynamic Digital Delay...") +        ddly_value = self.divide_to_cnth_cntl_reg(self.clkout_divider+1) if num_shifts >= 0 else self.divide_to_cnth_cntl_reg(self.clkout_divider-1) +        ddly_value_sysref = self.sysref_divider+1 if num_shifts >= 0 else self.sysref_divider-1          self.pokes8((              (0x141, 0xB1), # Dynamic digital delay enable on outputs 0, 8, 10              (0x143, 0x53), # SYSREF_CLR; SYNC Enabled; SYNC from pulser @ regwrite @@ -225,13 +255,12 @@ class LMK04828Mg(LMK04828):              (0x122, ddly_value), # Hidden register. Write the same as previous based on inc/dec.              (0x129, ddly_value), # Set DDLY values for DCLKout10 +/-1 on low cnt              (0x12A, ddly_value), # Hidden register. Write the same as previous based on inc/dec. -            (0x13C, ddly_value_sysref_reg0), # SYSREF DDLY value -            (0x13D, ddly_value_sysref_reg1), # SYSREF DDLY value +            (0x13C, (ddly_value_sysref & 0x1F00) >> 8), # SYSREF DDLY value [12:8] +            (0x13D, (ddly_value_sysref & 0x00FF) >> 0), # SYSREF DDLY value [7:0]              (0x144, 0x4E), # Enable SYNC on outputs 0, 8, 10          ))          for x in range(abs(num_shifts)):              self.poke8(0x142, 0x1)          # Put everything back the way it was before shifting.          self.poke8(0x144, 0xFF) # Disable SYNC on all outputs including SYSREF -        # self.poke8(0x143, 0xD2) # Reset SYSREF engine to proper SYNC settings          self.poke8(0x143, 0x52) # Pulser selected; SYNC enabled; 1 shot enabled  | 
