]> sigrok.org Git - libsigrokdecode.git/blame - decoders/xfp/pd.py
all decoders: introduce a reset() method
[libsigrokdecode.git] / decoders / xfp / pd.py
CommitLineData
845f0d40
BV
1##
2## This file is part of the libsigrokdecode project.
3##
4## Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
5##
6## This program is free software; you can redistribute it and/or modify
7## it under the terms of the GNU General Public License as published by
8## the Free Software Foundation; either version 3 of the License, or
9## (at your option) any later version.
10##
11## This program is distributed in the hope that it will be useful,
12## but WITHOUT ANY WARRANTY; without even the implied warranty of
13## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14## GNU General Public License for more details.
15##
16## You should have received a copy of the GNU General Public License
17## along with this program; if not, see <http://www.gnu.org/licenses/>.
18##
19
845f0d40 20import sigrokdecode as srd
135b790c 21from common.plugtrx import (MODULE_ID, ALARM_THRESHOLDS, AD_READOUTS, GCS_BITS,
1049c90f
BV
22 CONNECTOR, TRANSCEIVER, SERIAL_ENCODING, XMIT_TECH, CDR, DEVICE_TECH,
23 ENHANCED_OPTS, AUX_TYPES)
845f0d40
BV
24
25class Decoder(srd.Decoder):
b197383c 26 api_version = 3
845f0d40
BV
27 id = 'xfp'
28 name = 'XFP'
29 longname = '10 Gigabit Small Form Factor Pluggable Module (XFP)'
30 desc = 'Data structure describing display device capabilities.'
31 license = 'gplv3+'
32 inputs = ['i2c']
33 outputs = ['xfp']
da9bcbd9
BV
34 annotations = (
35 ('fieldnames-and-values', 'XFP structure field names and values'),
36 ('fields', 'XFP structure fields'),
37 )
845f0d40 38
92b7b49f 39 def __init__(self):
10aeb8ea
GS
40 self.reset()
41
42 def reset(self):
845f0d40
BV
43 # Received data items, used as an index into samplenum/data
44 self.cnt = -1
45 # Start/end sample numbers per data item
46 self.sn = []
47 # Multi-byte structure buffer
48 self.buf = []
49 # Filled in by address 0x7f in low memory
50 self.cur_highmem_page = 0
51 # Filled in by extended ID value in table 2
52 self.have_clei = False
53 # Handlers for each field in the structure, keyed by the end
54 # index of that field. Each handler is fed all unhandled bytes
55 # up until that point, so mark unused space with the dummy
56 # handler self.ignore().
57 self.MAP_LOWER_MEMORY = {
58 0: self.module_id,
59 1: self.signal_cc,
60 57: self.alarm_warnings,
61 59: self.vps,
62 69: self.ignore,
63 71: self.ber,
64 75: self.wavelength_cr,
65 79: self.fec_cr,
66 95: self.int_ctrl,
67 109: self.ad_readout,
68 111: self.gcs,
69 117: self.ignore,
70 118: self.ignore,
71 122: self.ignore,
72 126: self.ignore,
73 127: self.page_select,
74 }
75 self.MAP_HIGH_TABLE_1 = {
76 128: self.module_id,
77 129: self.ext_module_id,
78 130: self.connector,
79 138: self.transceiver,
80 139: self.serial_encoding,
81 140: self.br_min,
82 141: self.br_max,
83 142: self.link_length_smf,
84 143: self.link_length_e50,
85 144: self.link_length_50um,
86 145: self.link_length_625um,
87 146: self.link_length_copper,
88 147: self.device_tech,
89 163: self.vendor,
90 164: self.cdr,
91 167: self.vendor_oui,
92 183: self.vendor_pn,
93 185: self.vendor_rev,
94 187: self.wavelength,
95 189: self.wavelength_tolerance,
96 190: self.max_case_temp,
97 191: self.ignore,
98 195: self.power_supply,
99 211: self.vendor_sn,
100 219: self.manuf_date,
101 220: self.diag_mon,
102 221: self.enhanced_opts,
103 222: self.aux_mon,
104 223: self.ignore,
105 255: self.maybe_ascii,
106 }
107
8915b346 108 def start(self):
be465111 109 self.out_ann = self.register(srd.OUTPUT_ANN)
845f0d40
BV
110
111 def decode(self, ss, es, data):
112 cmd, data = data
113
114 # We only care about actual data bytes that are read (for now).
115 if cmd != 'DATA READ':
116 return
117
118 self.cnt += 1
119 self.sn.append([ss, es])
120
121 self.buf.append(data)
122 if self.cnt < 0x80:
123 if self.cnt in self.MAP_LOWER_MEMORY:
124 self.MAP_LOWER_MEMORY[self.cnt](self.buf)
125 self.buf.clear()
126 elif self.cnt < 0x0100 and self.cur_highmem_page == 0x01:
127 # Serial ID memory map
128 if self.cnt in self.MAP_HIGH_TABLE_1:
129 self.MAP_HIGH_TABLE_1[self.cnt](self.buf)
130 self.buf.clear()
131
132 # Annotation helper
133 def annotate(self, key, value, start_cnt=None, end_cnt=None):
134 if start_cnt is None:
135 start_cnt = self.cnt - len(self.buf) + 1
136 if end_cnt is None:
137 end_cnt = self.cnt
138 self.put(self.sn[start_cnt][0], self.sn[end_cnt][1],
139 self.out_ann, [0, [key + ": " + value]])
140 self.put(self.sn[start_cnt][0], self.sn[end_cnt][1],
141 self.out_ann, [1, [value]])
142
143 # Placeholder handler, needed to advance the buffer past unused or
144 # reserved space in the structures.
145 def ignore(self, data):
146 pass
147
148 # Show as ASCII if possible
149 def maybe_ascii(self, data):
150 for i in range(len(data)):
151 if data[i] >= 0x20 and data[i] < 0x7f:
152 cnt = self.cnt - len(data) + 1
153 self.annotate("Vendor ID", chr(data[i]), cnt, cnt)
154
155 # Convert 16-bit two's complement values, with each increment
868fd207 156 # representing 1/256C, to degrees Celsius.
845f0d40
BV
157 def to_temp(self, value):
158 if value & 0x8000:
159 value = -((value ^ 0xffff) + 1)
160 temp = value / 256.0
161 return "%.1f C" % temp
162
163 # TX bias current in uA. Each increment represents 0.2uA
164 def to_current(self, value):
165 current = value / 500000.0
166 return "%.1f mA" % current
167
168 # Power in mW, with each increment representing 0.1uW
169 def to_power(self, value):
170 power = value / 10000.0
171 return "%.2f mW" % power
172
173 # Wavelength in increments of 0.05nm
174 def to_wavelength(self, value):
175 wl = value / 20
176 return "%d nm" % wl
177
178 # Wavelength in increments of 0.005nm
179 def to_wavelength_tolerance(self, value):
180 wl = value / 200.0
181 return "%.1f nm" % wl
182
183 def module_id(self, data):
184 self.annotate("Module identifier", MODULE_ID.get(data[0], "Unknown"))
185
186 def signal_cc(self, data):
187 # No good data available.
188 if (data[0] != 0x00):
189 self.annotate("Signal Conditioner Control", "%.2x" % data[0])
190
191 def alarm_warnings(self, data):
192 cnt_idx = self.cnt - len(data)
193 idx = 0
194 while idx < 56:
195 if idx == 8:
196 # Skip over reserved A/D flag thresholds
197 idx += 8
198 value = (data[idx] << 8) | data[idx + 1]
199 if value != 0:
200 name = ALARM_THRESHOLDS.get(idx, "...")
201 if idx in (0, 2, 4, 6):
202 self.annotate(name, self.to_temp(value),
203 cnt_idx + idx, cnt_idx + idx + 1)
204 elif idx in (16, 18, 20, 22):
205 self.annotate(name, self.to_current(value),
206 cnt_idx + idx, cnt_idx + idx + 1)
207 elif idx in (24, 26, 28, 30, 32, 34, 36, 38):
208 self.annotate(name, self.to_power(value),
209 cnt_idx + idx, cnt_idx + idx + 1)
210 else:
211 self.annotate(name, "%d" % name, value, cnt_idx + idx,
212 cnt_idx + idx + 1)
213 idx += 2
214
215 def vps(self, data):
216 # No good data available.
217 if (data != [0, 0]):
218 self.annotate("VPS", "%.2x%.2x" % (data[0], data[1]))
219
220 def ber(self, data):
221 # No good data available.
222 if (data != [0, 0]):
223 self.annotate("BER", str(data))
224
225 def wavelength_cr(self, data):
226 # No good data available.
227 if (data != [0, 0, 0, 0]):
228 self.annotate("WCR", str(data))
229
230 def fec_cr(self, data):
231 if (data != [0, 0, 0, 0]):
232 self.annotate("FEC", str(data))
233
234 def int_ctrl(self, data):
235 # No good data available. Also boring.
236 out = []
237 for d in data:
238 out.append("%.2x" % d)
239 self.annotate("Interrupt bits", ' '.join(out))
240
241 def ad_readout(self, data):
242 cnt_idx = self.cnt - len(data) + 1
243 idx = 0
244 while idx < 14:
245 if idx == 2:
246 # Skip over reserved field
247 idx += 2
248 value = (data[idx] << 8) | data[idx + 1]
249 name = AD_READOUTS.get(idx, "...")
250 if value != 0:
251 if idx == 0:
252 self.annotate(name, self.to_temp(value),
253 cnt_idx + idx, cnt_idx + idx + 1)
254 elif idx == 4:
255 self.annotate(name, self.to_current(value),
256 cnt_idx + idx, cnt_idx + idx + 1)
257 elif idx in (6, 8):
258 self.annotate(name, self.to_power(value),
259 cnt_idx + idx, cnt_idx + idx + 1)
260 else:
261 self.annotate(name, str(value), cnt_idx + idx,
262 cnt_idx + idx + 1)
263 idx += 2
264
265 def gcs(self, data):
266 allbits = (data[0] << 8) | data[1]
267 out = []
268 for b in range(13):
269 if allbits & 0x8000:
270 out.append(GCS_BITS[b])
271 allbits <<= 1
272 self.annotate("General Control/Status", ', '.join(out))
273
274 def page_select(self, data):
275 self.cur_highmem_page = data[0]
276
277 def ext_module_id(self, data):
278 out = ["Power level %d module" % ((data[0] >> 6) + 1)]
279 if data[0] & 0x20 == 0:
280 out.append("CDR")
281 if data[0] & 0x10 == 0:
282 out.append("TX ref clock input required")
283 if data[0] & 0x08 == 0:
284 self.have_clei = True
285 self.annotate("Extended id", ', '.join(out))
286
287 def connector(self, data):
288 if data[0] in CONNECTOR:
289 self.annotate("Connector", CONNECTOR[data[0]])
290
291 def transceiver(self, data):
292 out = []
293 for t in range(8):
294 if data[t] == 0:
295 continue
296 value = data[t]
297 for b in range(8):
298 if value & 0x80:
299 if len(TRANSCEIVER[t]) < b + 1:
300 out.append("(unknown)")
301 else:
302 out.append(TRANSCEIVER[t][b])
303 value <<= 1
304 self.annotate("Transceiver compliance", ', '.join(out))
305
306 def serial_encoding(self, data):
307 out = []
308 value = data[0]
309 for b in range(8):
310 if value & 0x80:
311 if len(SERIAL_ENCODING) < b + 1:
312 out.append("(unknown)")
313 else:
314 out.append(SERIAL_ENCODING[b])
315 value <<= 1
316 self.annotate("Serial encoding support", ', '.join(out))
317
318 def br_min(self, data):
319 # Increments represent 100Mb/s
320 rate = data[0] / 10.0
321 self.annotate("Minimum bit rate", "%.3f GB/s" % rate)
322
323 def br_max(self, data):
324 # Increments represent 100Mb/s
325 rate = data[0] / 10.0
326 self.annotate("Maximum bit rate", "%.3f GB/s" % rate)
327
328 def link_length_smf(self, data):
329 if data[0] == 0:
330 length = "(standard)"
331 elif data[0] == 255:
332 length = "> 254 km"
333 else:
334 length = "%d km" % data[0]
335 self.annotate("Link length (SMF)", length)
336
337 def link_length_e50(self, data):
338 if data[0] == 0:
339 length = "(standard)"
340 elif data[0] == 255:
341 length = "> 508 m"
342 else:
343 length = "%d m" % (data[0] * 2)
344 self.annotate("Link length (extended, 50μm MMF)", length)
345
346 def link_length_50um(self, data):
347 if data[0] == 0:
348 length = "(standard)"
349 elif data[0] == 255:
350 length = "> 254 m"
351 else:
352 length = "%d m" % data[0]
353 self.annotate("Link length (50μm MMF)", length)
354
355 def link_length_625um(self, data):
356 if data[0] == 0:
357 length = "(standard)"
358 elif data[0] == 255:
359 length = "> 254 m"
360 else:
361 length = "%d m" % (data[0])
362 self.annotate("Link length (62.5μm MMF)", length)
363
364 def link_length_copper(self, data):
365 if data[0] == 0:
366 length = "(unknown)"
367 elif data[0] == 255:
368 length = "> 254 m"
369 else:
370 length = "%d m" % (data[0] * 2)
371 self.annotate("Link length (copper)", length)
372
373 def device_tech(self, data):
374 out = []
375 xmit = data[0] >> 4
376 if xmit <= len(XMIT_TECH) - 1:
377 out.append("%s transmitter" % XMIT_TECH[xmit])
378 dev = data[0] & 0x0f
379 for b in range(4):
380 out.append(DEVICE_TECH[b][(dev >> (3 - b)) & 0x01])
381 self.annotate("Device technology", ', '.join(out))
382
383 def vendor(self, data):
384 name = bytes(data).strip().decode('ascii').strip('\x00')
385 if name:
386 self.annotate("Vendor", name)
387
388 def cdr(self, data):
389 out = []
390 value = data[0]
391 for b in range(8):
392 if value & 0x80:
393 out.append(CDR[b])
394 value <<= 1
395 self.annotate("CDR support", ', '.join(out))
396
397 def vendor_oui(self, data):
398 if data != [0, 0, 0]:
399 self.annotate("Vendor OUI", "%.2X-%.2X-%.2X" % tuple(data))
400
401 def vendor_pn(self, data):
402 name = bytes(data).strip().decode('ascii').strip('\x00')
403 if name:
404 self.annotate("Vendor part number", name)
405
406 def vendor_rev(self, data):
407 name = bytes(data).strip().decode('ascii').strip('\x00')
408 if name:
409 self.annotate("Vendor revision", name)
410
411 def wavelength(self, data):
412 value = (data[0] << 8) | data[1]
413 self.annotate("Wavelength", self.to_wavelength(value))
414
415 def wavelength_tolerance(self, data):
416 value = (data[0] << 8) | data[1]
417 self.annotate("Wavelength tolerance", self.to_wavelength_tolerance(value))
418
419 def max_case_temp(self, data):
420 self.annotate("Maximum case temperature", "%d C" % data[0])
421
422 def power_supply(self, data):
423 out = []
424 self.annotate("Max power dissipation",
425 "%.3f W" % (data[0] * 0.02), self.cnt - 3, self.cnt - 3)
426 self.annotate("Max power dissipation (powered down)",
427 "%.3f W" % (data[1] * 0.01), self.cnt - 2, self.cnt - 2)
428 value = (data[2] >> 4) * 0.050
429 self.annotate("Max current required (5V supply)",
430 "%.3f A" % value, self.cnt - 1, self.cnt - 1)
431 value = (data[2] & 0x0f) * 0.100
432 self.annotate("Max current required (3.3V supply)",
433 "%.3f A" % value, self.cnt - 1, self.cnt - 1)
434 value = (data[3] >> 4) * 0.100
435 self.annotate("Max current required (1.8V supply)",
436 "%.3f A" % value, self.cnt, self.cnt)
437 value = (data[3] & 0x0f) * 0.050
438 self.annotate("Max current required (-5.2V supply)",
439 "%.3f A" % value, self.cnt, self.cnt)
440
441 def vendor_sn(self, data):
442 name = bytes(data).strip().decode('ascii').strip('\x00')
443 if name:
444 self.annotate("Vendor serial number", name)
445
446 def manuf_date(self, data):
447 y = int(bytes(data[0:2])) + 2000
448 m = int(bytes(data[2:4]))
449 d = int(bytes(data[4:6]))
450 mnf = "%.4d-%.2d-%.2d" % (y, m, d)
451 lot = bytes(data[6:]).strip().decode('ascii').strip('\x00')
452 if lot:
453 mnf += " lot " + lot
454 self.annotate("Manufacturing date", mnf)
455
456 def diag_mon(self, data):
457 out = []
458 if data[0] & 0x10:
459 out.append("BER support")
460 else:
461 out.append("no BER support")
462 if data[0] & 0x08:
463 out.append("average power measurement")
464 else:
465 out.append("OMA power measurement")
466 self.annotate("Diagnostic monitoring", ', '.join(out))
467
468 def enhanced_opts(self, data):
469 out = []
470 value = data[0]
471 for b in range(8):
472 if value & 0x80:
473 out.append(ENHANCED_OPTS[b])
474 value <<= 1
475 self.annotate("Enhanced option support", ', '.join(out))
476
477 def aux_mon(self, data):
478 aux = AUX_TYPES[data[0] >> 4]
479 self.annotate("AUX1 monitoring", aux)
480 aux = AUX_TYPES[data[0] & 0x0f]
481 self.annotate("AUX2 monitoring", aux)