1#
2# Copyright 2017 Ettus Research, a National Instruments Company
3#
4# SPDX-License-Identifier: GPL-3.0-or-later
5#
6"""
7LMK04828 driver for use with Magnesium
8"""
9
10import time
11from usrp_mpm.chips import LMK04828
12
13class LMK04828EISCAT(LMK04828):
14    """
15    LMK04828 controls for EISCAT daughterboard
16    """
17    def __init__(self, regs_iface, ref_clock_freq, slot=None, log=None):
18        LMK04828.__init__(self, regs_iface, log)
19        self.log.trace("Using reference clock frequency {} MHz".format(ref_clock_freq/1e6))
20        if ref_clock_freq != 10e6:
21            error_msg = "Invalid reference clock frequency: {} MHz. " \
22                        "Must be 10 MHz.".format(ref_clock_freq)
23            self.log.error(error_msg)
24            raise RuntimeError(error_msg)
25        self.ref_clock_freq = ref_clock_freq
26        self.init()
27        self.config()
28
29
30    def get_vco_freq(self):
31        """
32        Return the hard coded VCO frequency in the LMK PLL2.
33        """
34        return 2.496e9
35
36    def init(self):
37        """
38        Basic init. Turns it on. Let's us read SPI.
39        """
40        self.log.debug("Reset LMK & Verify")
41        self.pokes8((
42            (0x000, 0x90), # Assert reset
43            (0x000, 0x10), # De-assert reset
44            (0x002, 0x00), # De-assert power down
45            (0x16E, 0x3B), # PLL2 Lock Detect Config as SDO
46        ))
47        if not self.verify_chip_id():
48            raise Exception("Unable to locate LMK04828")
49
50
51    def config(self):
52        """
53        Write lots of config foo.
54        """
55        self.log.trace("LMK Initialization")
56        clkin0_r_divider = {10e6: 0x0A, 20e6: 0x14}[self.ref_clock_freq]
57        self.pokes8((
58            (0x100, 0x6C), # CLKout Config
59            (0x101, 0x66), # CLKout Config
60            (0x102, 0x66), # CLKout Config
61            (0x103, 0x00), # CLKout Config
62            (0x104, 0x20), # CLKout Config
63            (0x105, 0x00), # CLKout Config
64            (0x106, 0xF3), # CLKout Config
65            (0x107, 0x05), # CLKout Config
66            (0x108, 0x6C), # CLKout Config
67            (0x109, 0x67), # CLKout Config
68            (0x10A, 0x67), # CLKout Config
69            (0x10B, 0x00), # CLKout Config
70            (0x10C, 0x20), # CLKout Config
71            (0x10D, 0x00), # CLKout Config
72            (0x10E, 0x71), # CLKout Config
73            (0x10F, 0x05), # CLKout Config
74            (0x110, 0x6C), # CLKout Config
75            (0x111, 0x67), # CLKout Config
76            (0x112, 0x67), # CLKout Config
77            (0x113, 0x00), # CLKout Config
78            (0x114, 0x20), # CLKout Config
79            (0x115, 0x00), # CLKout Config
80            (0x116, 0x71), # CLKout Config
81            (0x117, 0x05), # CLKout Config
82            (0x118, 0x6C), # CLKout Config
83            (0x119, 0x67), # CLKout Config
84            (0x11A, 0x67), # CLKout Config
85            (0x11B, 0x00), # CLKout Config
86            (0x11C, 0x20), # CLKout Config
87            (0x11D, 0x00), # CLKout Config
88            (0x11E, 0x71), # CLKout Config
89            (0x11F, 0x05), # CLKout Config
90            (0x120, 0x78), # CLKout Config
91            (0x121, 0x66), # CLKout Config
92            (0x122, 0x66), # CLKout Config
93            (0x123, 0x00), # CLKout Config
94            (0x124, 0x20), # CLKout Config
95            (0x125, 0x00), # CLKout Config
96            (0x126, 0xF3), # CLKout Config
97            (0x127, 0x00), # CLKout Config
98            (0x128, 0x6C), # CLKout Config
99            (0x129, 0x55), # CLKout Config
100            (0x12A, 0x55), # CLKout Config
101            (0x12B, 0x00), # CLKout Config
102            (0x12C, 0x20), # CLKout Config
103            (0x12D, 0x00), # CLKout Config
104            (0x12E, 0xF9), # CLKout Config
105            (0x12F, 0x00), # CLKout Config
106            (0x130, 0x6C), # CLKout Config
107            (0x131, 0x67), # CLKout Config
108            (0x132, 0x67), # CLKout Config
109            (0x133, 0x00), # CLKout Config
110            (0x134, 0x20), # CLKout Config
111            (0x135, 0x00), # CLKout Config
112            (0x136, 0x71), # CLKout Config
113            (0x137, 0x01), # CLKout Config
114            (0x138, 0x10), # VCO_MUX to VCO 1; OSCout off
115            (0x139, 0x00), # SYSREF Source = MUX; SYSREF MUX = Normal SYNC
116            (0x13A, 0x01), # SYSREF Divide [12:8]
117            (0x13B, 0xE0), # SYSREF Divide [7:0]
118            (0x13C, 0x00), # SYSREF DDLY [12:8]
119            (0x13D, 0x08), # SYSREF DDLY [7:0] ... 8 is default, <8 is reserved
120            (0x13E, 0x00), # SYSREF Pulse Count = 1 pulse/request
121            (0x13F, 0x0B), # Feedback Mux: Enabled, DCLKout6, drives PLL1N divider
122            (0x140, 0x00), # POWERDOWN options
123            (0x141, 0x08), # Dynamic digital delay enable
124            (0x142, 0x00), # Dynamic digital delay step
125            (0x143, 0xD1), # SYNC edge sensitive; SYSREF_CLR; SYNC Enabled; SYNC fro
126            (0x144, 0x00), # Enable SYNC on all outputs including sysref
127            (0x145, 0x7F), # Always program to d127
128            (0x146, 0x08), # CLKin Type & En
129            (0x147, 0x0E), # CLKin_SEL = CLKin1 manual; CLKin1 to PLL1
130            (0x148, 0x01), # CLKin_SEL0 = input with pullup
131            (0x149, 0x01), # CLKin_SEL1 = input with pulldown
132            (0x14A, 0x02), # RESET type as input w/pulldown
133            (0x14B, 0x01), # Holdover & DAC Manual Mode
134            (0x14C, 0xF6), # DAC Manual Mode
135            (0x14D, 0x00), # DAC Settings (defaults)
136            (0x14E, 0x00), # DAC Settings (defaults)
137            (0x14F, 0x7F), # DAC Settings (defaults)
138            (0x150, 0x00), # Holdover Settings; bits 0/1 = '0' per long PLL1 lock time debug
139            (0x151, 0x02), # Holdover Settings (defaults)
140            (0x152, 0x00), # Holdover Settings (defaults)
141            (0x153, 0x00), # CLKin0_R divider [13:8], default = 0
142            (0x154, clkin0_r_divider), # CLKin0_R divider [7:0], default = d120
143            (0x155, 0x00), # CLKin1_R divider [13:8], default = 0
144            (0x156, 0x01), # CLKin1_R divider [7:0], default = d120
145            (0x157, 0x00), # CLKin2_R divider [13:8], default = 0
146            (0x158, 0x01), # CLKin2_R divider [7:0], default = d120
147            (0x159, 0x00), # PLL1 N divider [13:8], default = 0
148            (0x15A, 0x68), # PLL1 N divider [7:0], default = d120
149            (0x15B, 0xCF), # PLL1 PFD
150            (0x15C, 0x27), # PLL1 DLD Count [13:8]
151            (0x15D, 0x10), # PLL1 DLD Count [7:0]
152            (0x15E, 0x00), # PLL1 R/N delay, defaults = 0
153            (0x15F, 0x13), # Status LD1 pin = PLL2 LD, push-pull output
154            (0x160, 0x00), # PLL2 R divider [11:8];
155            (0x161, 0x01), # PLL2 R divider [7:0]
156            (0x162, 0x24), # PLL2 prescaler; OSCin freq
157            (0x163, 0x00), # PLL2 Cal = PLL2 normal val
158            (0x164, 0x00), # PLL2 Cal = PLL2 normal val
159            (0x165, 0x0C), # PLL2 Cal = PLL2 normal val
160            (0x171, 0xAA), # Write this val after x165
161            (0x172, 0x02), # Write this val after x165
162            (0x17C, 0x15), # VCo1 Cal; write before x168
163            (0x17D, 0x33), # VCo1 Cal; write before x168
164            (0x166, 0x00), # PLL2 N[17:16]
165            (0x167, 0x00), # PLL2 N[15:8]
166            (0x168, 0x0C), # PLL2 N[7:0]
167            (0x169, 0x51), # PLL2 PFD
168            (0x16A, 0x27), # PLL2 DLD Count [13:8] = default d32
169            (0x16B, 0x10), # PLL2 DLD Count [7:0] = default d0
170            (0x16C, 0x00), # PLL2 Loop filter r = 200 ohm
171            (0x16D, 0x00), # PLL2 loop filter c = 10 pF
172            (0x173, 0x00), # Do not power down PLL2 or prescaler
173        ))
174        # TODO: change to Polling.
175        time.sleep(1.0) # Increased time to wait for DAC and VCXO to settle.
176        self.pokes8((
177            (0x182, 0x1), # Clear Lock Detect Sticky
178            (0x182, 0x0), # Clear Lock Detect Sticky
179            (0x183, 0x1), # Clear Lock Detect Sticky
180            (0x183, 0x0), # Clear Lock Detect Sticky
181        ))
182        time.sleep(0.1)
183        if not self.check_plls_locked():
184            raise RuntimeError("At least one LMK PLL did not lock! Check the logs for details.")
185        self.log.trace("Setting SYNC and SYSREF config...")
186        self.pokes8((
187            (0x143, 0xF1), # toggle SYNC polarity to trigger SYNC event
188            (0x143, 0xD1), # toggle SYNC polarity to trigger SYNC event
189            (0x139, 0x02), # SYSREF Source = MUX; SYSREF MUX = pulser
190            (0x144, 0xFF), # Disable SYNC on all outputs including sysref
191            (0x143, 0x52), # Pulser selected; SYNC enabled; 1 shot enabled
192        ))
193        self.log.debug("LMK init'd and locked!")
194
195    def lmk_shift(self, num_shifts=0):
196        """
197        Apply time shift
198
199        TODO: See if we can move this up to parent class
200        """
201        ddly_value = 0x67 if num_shifts >= 0 else 0x65
202        self.pokes8((
203            (0x141, 0x4E), # Dynamic digital delay enable
204            (0x143, 0x53), # SYSREF_CLR; SYNC Enabled; SYNC from pulser @ regwrite
205            (0x139, 0x02), # SYSREF_MUX = Pulser
206            (0x109, ddly_value), # Set DDLY values for DCLKout2 +/-1 on low cnt.
207                                 # To Increment phase, write 0x65. Decrement = 0x67
208            (0x10A, ddly_value), # Hidden register. Write the same as previous based on inc/dec.
209            (0x111, ddly_value), # Set DDLY values for DCLKout4 +/-1 on low cnt
210            (0x112, ddly_value), # Hidden register. Write the same as previous based on inc/dec.
211            (0x119, ddly_value), # Set DDLY values for DCLKout6 +/-1 on low cnt
212            (0x11A, ddly_value), # Hidden register. Write the same as previous based on inc/dec.
213            (0x131, ddly_value), # Set DDLY values for DCLKout12 +/-1 on low cnt
214            (0x132, ddly_value), # Hidden register. Write the same as previous based on inc/dec.
215            (0x144, 0xB1), # Enable SYNC on outputs 2,4,6,12
216        ))
217        for x in range(abs(num_shifts)):
218            self.poke8(0x142, 0x1)
219        self.poke8(0x144, 0xFF) # Disable SYNC on all outputs
220
221