1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
|
#
# Copyright 2020 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
"""
LMK05318 parent driver class
"""
import time
import datetime
from usrp_mpm.mpmlog import get_logger
class LMK05318:
"""
Generic driver class for LMK05318 access.
"""
LMK_VENDOR_ID = 0x100B
LMK_PROD_ID = 0x35
LMK_EEPROM_REG_COMMIT = "register_commit"
LMK_EEPROM_DIRECT_WRITE = "direct_write"
def __init__(self, regs_iface, parent_log=None):
self.log = \
parent_log.getChild("LMK05318") if parent_log is not None \
else get_logger("LMK05318")
self.regs_iface = regs_iface
assert hasattr(self.regs_iface, 'peek8')
assert hasattr(self.regs_iface, 'poke8')
self.peek8 = regs_iface.peek8
def poke8(self, addr, val, overwrite_mask=False):
"""
Write val to addr via register interface
"""
# TI LMK UserGuide chapter 9.5.5 states that some register require bit masks
# to be applied to bits to avoid writing to them
# mask is in the form that a 1 means that the bit shall not be modified
# in order to write to the address without the mask applied
# the overwrite_mask parameter can be set to True
if not overwrite_mask:
mask = None
if addr == 0x0C:
mask = 0xA7
elif addr == 0x9D:
mask = 0xFF
elif addr == 0xA4:
mask = 0xFF
elif addr in range(0x161, 0x1B3):
mask = 0xFF
if mask is not None:
current_val = self.peek8(addr)
val = val & ~mask
val = val | current_val
self.log.trace(
"Attention: writing to register {:02x} with masked bits, "
"mask 0x{:02x} was applied, resulting in value {:02x}"
.format(addr, mask, val))
self.regs_iface.poke8(addr, val)
def pokes8(self, addr_vals, overwrite_mask=False):
"""
Apply a series of pokes.
pokes8((0,1),(0,2)) is the same as calling poke8(0,1), poke8(0,2).
"""
for addr, val in addr_vals:
self.poke8(addr, val, overwrite_mask)
def get_vendor_id(self):
""" Read back the chip's vendor ID"""
vendor_id_high = self.peek8(0x00)
vendor_id_low = self.peek8(0x01)
vendor_id = (vendor_id_high << 8) \
| vendor_id_low
self.log.trace("Vendor ID Readback: 0x{:X}".format(vendor_id))
return vendor_id
def get_product_id(self):
"""
Read back the chip's product ID
"""
prod_id = self.peek8(0x02)
self.log.trace("Product ID Readback: 0x{:X}".format(prod_id))
return prod_id
def is_chip_id_valid(self):
"""
Returns True if the chip ID and product ID matches what we expect,
False otherwise.
"""
vendor_id = self.get_vendor_id()
prod_id = self.get_product_id()
if vendor_id != self.LMK_VENDOR_ID:
self.log.error("Wrong Vendor ID 0x{:X}".format(vendor_id))
return False
if prod_id != self.LMK_PROD_ID:
self.log.error("Wrong Product ID 0x{:X}".format(prod_id))
return False
return True
def soft_reset(self, value=True):
"""
Performs a soft reset of the LMK05318 by setting or unsetting
the reset register
"""
reset_addr = 0xC #DEV_CTL
if value: # Reset
reset_byte = 0x80
else: # Clear Reset
reset_byte = 0x7F & self.peek8(reset_addr)
self.poke8(reset_addr, reset_byte, overwrite_mask=True)
def write_cfg_regs_to_eeprom(self, method, eeprom_data=None):
"""
program the current register config to LMK eeprom
"""
def _wait_for_busy(self, value):
wait_until = datetime.datetime.now() + datetime.timedelta(seconds=2)
while datetime.datetime.now() < wait_until:
self.log.trace("wait till busy bit becomes {}".format(value))
busy = (self.peek8(0x9D) >> 2) & 1 # check if busy bit is cleared
if busy == value:
return True
time.sleep(0.01)
return False
if method == self.LMK_EEPROM_REG_COMMIT:
self.log.trace("write current device register content to EEPROM")
#store current cfg to SRAM
self.poke8(0x9D, 0x40, overwrite_mask=True)
time.sleep(0.01)
#unlock EEPROM
self.poke8(0xA4, 0xEA, overwrite_mask=True)
time.sleep(0.01)
#store SRAM into EEPROM
self.poke8(0x9D, 0x03, overwrite_mask=True)
#the actual programming takes about 230ms, poll the busy bit to see when it's done
if not _wait_for_busy(self, 1):
self.log.error("EEPROM does not start programming, something went wrong")
if not _wait_for_busy(self, 0):
self.log.error("EEPROM is still busy after programming, something went wrong")
elif method == self.LMK_EEPROM_DIRECT_WRITE:
raise RuntimeError("direct LMK05318 EEPROM programming not implemented")
else:
raise RuntimeError("Invalid method for LMK05318 EEPROM programming")
#lock EEPROM
self.poke8(0xA4, 0x00, overwrite_mask=True)
self.log.trace("programming EEPROM done, power-cycle or hard-reset to take effect")
def write_eeprom_to_cfg_regs(self):
"""
read register cfg from eeprom and store it into registers
"""
self.poke8(0x9D, 0x08, overwrite_mask=True)
def get_eeprom_prog_cycles(self):
"""
returns the number of eeprom programming cycles
note:
the actual counter only increases after programming AND power-cycle/hard-reset
so multiple programming cycles without power cycle will lead to wrong counter values
"""
return self.peek8(0x9C)
def get_status_dpll(self):
"""
returns the status register of the DPLL as human readable string
"""
status = self.peek8(0x0E)
return f"""
Loss of phase lock: {status>>7 & 1}
Loss of freq. lock: {status>>6 & 1}
Tuning word update: {status>>5 & 1}
Holdover Event: {status>>4 & 1}
Reference Switch Event: {status>>3 & 1}
Active ref. missing clk: {status>>2 & 1}
Active ref. loss freq.: {status>>1 & 1}
Active ref. loss ampl.: {status & 1}
"""
def get_status_pll_xo(self):
"""
returns the status register of the PLLs and XO as human readable string
"""
status = self.peek8(0x0D)
return f"""
Loss of freq. detection XO: {status>>4 & 1}
Loss of lock APLL2: {status>>3 & 1}
Loss of lock APLL1: {status>>2 & 1}
Loss of source XO: {status & 1}
"""
|