]> sigrok.org Git - libsigrokdecode.git/blame - decoders/xfp/pd.py
sdcard_spi: Use proper annotation classes.
[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
BV
20import sigrokdecode as srd
21import os
22
23MODULE_ID = {
24 0x01: 'GBIC',
25 0x02: 'Integrated module/connector',
26 0x03: 'SFP',
27 0x04: '300-pin XBI',
28 0x05: 'XENPAK',
29 0x06: 'XFP',
30 0x07: 'XFF',
31 0x08: 'XFP-E',
32 0x09: 'XPAK',
33 0x0a: 'X2',
34}
35
36ALARM_THRESHOLDS = {
37 0: "Temp high alarm",
38 2: "Temp low alarm",
39 4: "Temp high warning",
40 6: "Temp low warning",
41 16: "Bias high alarm",
42 18: "Bias low alarm",
43 20: "Bias high warning",
44 22: "Bias low warning",
45 24: "TX power high alarm",
46 26: "TX power low alarm",
47 28: "TX power high warning",
48 30: "TX power low warning",
49 32: "RX power high alarm",
50 34: "RX power low alarm",
51 36: "RX power high warning",
52 38: "RX power low warning",
53 40: "AUX 1 high alarm",
54 42: "AUX 1 low alarm",
55 44: "AUX 1 high warning",
56 46: "AUX 1 low warning",
57 48: "AUX 2 high alarm",
58 50: "AUX 2 low alarm",
59 52: "AUX 2 high warning",
60 54: "AUX 2 low warning",
61}
62
63AD_READOUTS = {
64 0: "Module temperature",
65 4: "TX bias current",
66 6: "Measured TX output power",
67 8: "Measured RX input power",
68 10: "AUX 1 measurement",
69 12: "AUX 2 measurement",
70}
71
72GCS_BITS = [
73 "TX disable",
74 "Soft TX disable",
75 "MOD_NR",
76 "P_Down",
77 "Soft P_Down",
78 "Interrupt",
79 "RX_LOS",
80 "Data_Not_Ready",
81 "TX_NR",
82 "TX_Fault",
83 "TX_CDR not locked",
84 "RX_NR",
85 "RX_CDR not locked",
86]
87
88CONNECTOR = {
89 0x01: "SC",
90 0x02: "Fibre Channel style 1 copper",
91 0x03: "Fibre Channel style 2 copper",
92 0x04: "BNC/TNC",
93 0x05: "Fibre Channel coax",
94 0x06: "FiberJack",
95 0x07: "LC",
96 0x08: "MT-RJ",
97 0x09: "MU",
98 0x0a: "SG",
99 0x0b: "Optical pigtail",
100 0x20: "HSSDC II",
101 0x21: "Copper pigtail",
102}
103
104TRANSCEIVER = [
105 # 10GB Ethernet
106 ["10GBASE-SR", "10GBASE-LR", "10GBASE-ER", "10GBASE-LRM", "10GBASE-SW",
107 "10GBASE-LW", "10GBASE-EW"],
108 # 10GB Fibre Channel
109 ["1200-MX-SN-I", "1200-SM-LL-L", "Extended Reach 1550 nm",
110 "Intermediate reach 1300 nm FP"],
111 # 10GB Copper
112 [],
113 # 10GB low speed
114 ["1000BASE-SX / 1xFC MMF", "1000BASE-LX / 1xFC SMF", "2xFC MMF",
115 "2xFC SMF", "OC48-SR", "OC48-IR", "OC48-LR"],
116 # 10GB SONET/SDH interconnect
117 ["I-64.1r", "I-64.1", "I-64.2r", "I-64.2", "I-64.3", "I-64.5"],
118 # 10GB SONET/SDH short haul
119 ["S-64.1", "S-64.2a", "S-64.2b", "S-64.3a", "S-64.3b", "S-64.5a", "S-64.5b"],
120 # 10GB SONET/SDH long haul
121 ["L-64.1", "L-64.2a", "L-64.2b", "L-64.2c", "L-64.3", "G.959.1 P1L1-2D2"],
122 # 10GB SONET/SDH very long haul
123 ["V-64.2a", "V-64.2b", "V-64.3"],
124]
125
126SERIAL_ENCODING = [
127 "64B/66B",
128 "8B/10B",
129 "SONET scrambled",
130 "NRZ",
131 "RZ",
132]
133
134XMIT_TECH = [
135 "850 nm VCSEL",
136 "1310 nm VCSEL",
137 "1550 nm VCSEL",
138 "1310 nm FP",
139 "1310 nm DFB",
140 "1550 nm DFB",
141 "1310 nm EML"
142 "1550 nm EML"
143 "copper",
144]
145
146CDR = [
147 "9.95Gb/s",
148 "10.3Gb/s",
149 "10.5Gb/s",
150 "10.7Gb/s",
151 "11.1Gb/s",
152 "(unknown)",
153 "lineside loopback mode",
154 "XFI loopback mode",
155]
156
157DEVICE_TECH = [
158 ["no wavelength control", "sctive wavelength control"],
159 ["uncooled transmitter device", "cooled transmitter"],
160 ["PIN detector", "APD detector"],
161 ["transmitter not tunable", "transmitter tunable"],
162]
163
164ENHANCED_OPTS = [
165 "VPS",
166 "soft TX_DISABLE",
167 "soft P_Down",
168 "VPS LV regulator mode",
169 "VPS bypassed regulator mode",
170 "active FEC control",
171 "wavelength tunability",
172 "CMU",
173]
174
175AUX_TYPES = [
176 "not implemented",
177 "APD bias voltage",
178 "(unknown)",
179 "TEC current",
180 "laser temperature",
181 "laser wavelength",
182 "5V supply voltage",
183 "3.3V supply voltage",
184 "1.8V supply voltage",
185 "-5.2V supply voltage",
186 "5V supply current",
187 "(unknown)",
188 "(unknown)",
189 "3.3V supply current",
190 "1.8V supply current",
191 "-5.2V supply current",
192]
193
194class Decoder(srd.Decoder):
195 api_version = 1
196 id = 'xfp'
197 name = 'XFP'
198 longname = '10 Gigabit Small Form Factor Pluggable Module (XFP)'
199 desc = 'Data structure describing display device capabilities.'
200 license = 'gplv3+'
201 inputs = ['i2c']
202 outputs = ['xfp']
203 probes = []
204 optional_probes = []
205 options = {}
206 annotations = [
9f2f42c0
UH
207 ['fieldnames-and-values', 'XFP structure field names and values'],
208 ['fields', 'XFP structure fields'],
845f0d40
BV
209 ]
210
211 def __init__(self, **kwargs):
212 # Received data items, used as an index into samplenum/data
213 self.cnt = -1
214 # Start/end sample numbers per data item
215 self.sn = []
216 # Multi-byte structure buffer
217 self.buf = []
218 # Filled in by address 0x7f in low memory
219 self.cur_highmem_page = 0
220 # Filled in by extended ID value in table 2
221 self.have_clei = False
222 # Handlers for each field in the structure, keyed by the end
223 # index of that field. Each handler is fed all unhandled bytes
224 # up until that point, so mark unused space with the dummy
225 # handler self.ignore().
226 self.MAP_LOWER_MEMORY = {
227 0: self.module_id,
228 1: self.signal_cc,
229 57: self.alarm_warnings,
230 59: self.vps,
231 69: self.ignore,
232 71: self.ber,
233 75: self.wavelength_cr,
234 79: self.fec_cr,
235 95: self.int_ctrl,
236 109: self.ad_readout,
237 111: self.gcs,
238 117: self.ignore,
239 118: self.ignore,
240 122: self.ignore,
241 126: self.ignore,
242 127: self.page_select,
243 }
244 self.MAP_HIGH_TABLE_1 = {
245 128: self.module_id,
246 129: self.ext_module_id,
247 130: self.connector,
248 138: self.transceiver,
249 139: self.serial_encoding,
250 140: self.br_min,
251 141: self.br_max,
252 142: self.link_length_smf,
253 143: self.link_length_e50,
254 144: self.link_length_50um,
255 145: self.link_length_625um,
256 146: self.link_length_copper,
257 147: self.device_tech,
258 163: self.vendor,
259 164: self.cdr,
260 167: self.vendor_oui,
261 183: self.vendor_pn,
262 185: self.vendor_rev,
263 187: self.wavelength,
264 189: self.wavelength_tolerance,
265 190: self.max_case_temp,
266 191: self.ignore,
267 195: self.power_supply,
268 211: self.vendor_sn,
269 219: self.manuf_date,
270 220: self.diag_mon,
271 221: self.enhanced_opts,
272 222: self.aux_mon,
273 223: self.ignore,
274 255: self.maybe_ascii,
275 }
276
8915b346 277 def start(self):
be465111 278 self.out_ann = self.register(srd.OUTPUT_ANN)
845f0d40
BV
279
280 def decode(self, ss, es, data):
281 cmd, data = data
282
283 # We only care about actual data bytes that are read (for now).
284 if cmd != 'DATA READ':
285 return
286
287 self.cnt += 1
288 self.sn.append([ss, es])
289
290 self.buf.append(data)
291 if self.cnt < 0x80:
292 if self.cnt in self.MAP_LOWER_MEMORY:
293 self.MAP_LOWER_MEMORY[self.cnt](self.buf)
294 self.buf.clear()
295 elif self.cnt < 0x0100 and self.cur_highmem_page == 0x01:
296 # Serial ID memory map
297 if self.cnt in self.MAP_HIGH_TABLE_1:
298 self.MAP_HIGH_TABLE_1[self.cnt](self.buf)
299 self.buf.clear()
300
301 # Annotation helper
302 def annotate(self, key, value, start_cnt=None, end_cnt=None):
303 if start_cnt is None:
304 start_cnt = self.cnt - len(self.buf) + 1
305 if end_cnt is None:
306 end_cnt = self.cnt
307 self.put(self.sn[start_cnt][0], self.sn[end_cnt][1],
308 self.out_ann, [0, [key + ": " + value]])
309 self.put(self.sn[start_cnt][0], self.sn[end_cnt][1],
310 self.out_ann, [1, [value]])
311
312 # Placeholder handler, needed to advance the buffer past unused or
313 # reserved space in the structures.
314 def ignore(self, data):
315 pass
316
317 # Show as ASCII if possible
318 def maybe_ascii(self, data):
319 for i in range(len(data)):
320 if data[i] >= 0x20 and data[i] < 0x7f:
321 cnt = self.cnt - len(data) + 1
322 self.annotate("Vendor ID", chr(data[i]), cnt, cnt)
323
324 # Convert 16-bit two's complement values, with each increment
325 # representing 1/256C, to degrees Celcius.
326 def to_temp(self, value):
327 if value & 0x8000:
328 value = -((value ^ 0xffff) + 1)
329 temp = value / 256.0
330 return "%.1f C" % temp
331
332 # TX bias current in uA. Each increment represents 0.2uA
333 def to_current(self, value):
334 current = value / 500000.0
335 return "%.1f mA" % current
336
337 # Power in mW, with each increment representing 0.1uW
338 def to_power(self, value):
339 power = value / 10000.0
340 return "%.2f mW" % power
341
342 # Wavelength in increments of 0.05nm
343 def to_wavelength(self, value):
344 wl = value / 20
345 return "%d nm" % wl
346
347 # Wavelength in increments of 0.005nm
348 def to_wavelength_tolerance(self, value):
349 wl = value / 200.0
350 return "%.1f nm" % wl
351
352 def module_id(self, data):
353 self.annotate("Module identifier", MODULE_ID.get(data[0], "Unknown"))
354
355 def signal_cc(self, data):
356 # No good data available.
357 if (data[0] != 0x00):
358 self.annotate("Signal Conditioner Control", "%.2x" % data[0])
359
360 def alarm_warnings(self, data):
361 cnt_idx = self.cnt - len(data)
362 idx = 0
363 while idx < 56:
364 if idx == 8:
365 # Skip over reserved A/D flag thresholds
366 idx += 8
367 value = (data[idx] << 8) | data[idx + 1]
368 if value != 0:
369 name = ALARM_THRESHOLDS.get(idx, "...")
370 if idx in (0, 2, 4, 6):
371 self.annotate(name, self.to_temp(value),
372 cnt_idx + idx, cnt_idx + idx + 1)
373 elif idx in (16, 18, 20, 22):
374 self.annotate(name, self.to_current(value),
375 cnt_idx + idx, cnt_idx + idx + 1)
376 elif idx in (24, 26, 28, 30, 32, 34, 36, 38):
377 self.annotate(name, self.to_power(value),
378 cnt_idx + idx, cnt_idx + idx + 1)
379 else:
380 self.annotate(name, "%d" % name, value, cnt_idx + idx,
381 cnt_idx + idx + 1)
382 idx += 2
383
384 def vps(self, data):
385 # No good data available.
386 if (data != [0, 0]):
387 self.annotate("VPS", "%.2x%.2x" % (data[0], data[1]))
388
389 def ber(self, data):
390 # No good data available.
391 if (data != [0, 0]):
392 self.annotate("BER", str(data))
393
394 def wavelength_cr(self, data):
395 # No good data available.
396 if (data != [0, 0, 0, 0]):
397 self.annotate("WCR", str(data))
398
399 def fec_cr(self, data):
400 if (data != [0, 0, 0, 0]):
401 self.annotate("FEC", str(data))
402
403 def int_ctrl(self, data):
404 # No good data available. Also boring.
405 out = []
406 for d in data:
407 out.append("%.2x" % d)
408 self.annotate("Interrupt bits", ' '.join(out))
409
410 def ad_readout(self, data):
411 cnt_idx = self.cnt - len(data) + 1
412 idx = 0
413 while idx < 14:
414 if idx == 2:
415 # Skip over reserved field
416 idx += 2
417 value = (data[idx] << 8) | data[idx + 1]
418 name = AD_READOUTS.get(idx, "...")
419 if value != 0:
420 if idx == 0:
421 self.annotate(name, self.to_temp(value),
422 cnt_idx + idx, cnt_idx + idx + 1)
423 elif idx == 4:
424 self.annotate(name, self.to_current(value),
425 cnt_idx + idx, cnt_idx + idx + 1)
426 elif idx in (6, 8):
427 self.annotate(name, self.to_power(value),
428 cnt_idx + idx, cnt_idx + idx + 1)
429 else:
430 self.annotate(name, str(value), cnt_idx + idx,
431 cnt_idx + idx + 1)
432 idx += 2
433
434 def gcs(self, data):
435 allbits = (data[0] << 8) | data[1]
436 out = []
437 for b in range(13):
438 if allbits & 0x8000:
439 out.append(GCS_BITS[b])
440 allbits <<= 1
441 self.annotate("General Control/Status", ', '.join(out))
442
443 def page_select(self, data):
444 self.cur_highmem_page = data[0]
445
446 def ext_module_id(self, data):
447 out = ["Power level %d module" % ((data[0] >> 6) + 1)]
448 if data[0] & 0x20 == 0:
449 out.append("CDR")
450 if data[0] & 0x10 == 0:
451 out.append("TX ref clock input required")
452 if data[0] & 0x08 == 0:
453 self.have_clei = True
454 self.annotate("Extended id", ', '.join(out))
455
456 def connector(self, data):
457 if data[0] in CONNECTOR:
458 self.annotate("Connector", CONNECTOR[data[0]])
459
460 def transceiver(self, data):
461 out = []
462 for t in range(8):
463 if data[t] == 0:
464 continue
465 value = data[t]
466 for b in range(8):
467 if value & 0x80:
468 if len(TRANSCEIVER[t]) < b + 1:
469 out.append("(unknown)")
470 else:
471 out.append(TRANSCEIVER[t][b])
472 value <<= 1
473 self.annotate("Transceiver compliance", ', '.join(out))
474
475 def serial_encoding(self, data):
476 out = []
477 value = data[0]
478 for b in range(8):
479 if value & 0x80:
480 if len(SERIAL_ENCODING) < b + 1:
481 out.append("(unknown)")
482 else:
483 out.append(SERIAL_ENCODING[b])
484 value <<= 1
485 self.annotate("Serial encoding support", ', '.join(out))
486
487 def br_min(self, data):
488 # Increments represent 100Mb/s
489 rate = data[0] / 10.0
490 self.annotate("Minimum bit rate", "%.3f GB/s" % rate)
491
492 def br_max(self, data):
493 # Increments represent 100Mb/s
494 rate = data[0] / 10.0
495 self.annotate("Maximum bit rate", "%.3f GB/s" % rate)
496
497 def link_length_smf(self, data):
498 if data[0] == 0:
499 length = "(standard)"
500 elif data[0] == 255:
501 length = "> 254 km"
502 else:
503 length = "%d km" % data[0]
504 self.annotate("Link length (SMF)", length)
505
506 def link_length_e50(self, data):
507 if data[0] == 0:
508 length = "(standard)"
509 elif data[0] == 255:
510 length = "> 508 m"
511 else:
512 length = "%d m" % (data[0] * 2)
513 self.annotate("Link length (extended, 50μm MMF)", length)
514
515 def link_length_50um(self, data):
516 if data[0] == 0:
517 length = "(standard)"
518 elif data[0] == 255:
519 length = "> 254 m"
520 else:
521 length = "%d m" % data[0]
522 self.annotate("Link length (50μm MMF)", length)
523
524 def link_length_625um(self, data):
525 if data[0] == 0:
526 length = "(standard)"
527 elif data[0] == 255:
528 length = "> 254 m"
529 else:
530 length = "%d m" % (data[0])
531 self.annotate("Link length (62.5μm MMF)", length)
532
533 def link_length_copper(self, data):
534 if data[0] == 0:
535 length = "(unknown)"
536 elif data[0] == 255:
537 length = "> 254 m"
538 else:
539 length = "%d m" % (data[0] * 2)
540 self.annotate("Link length (copper)", length)
541
542 def device_tech(self, data):
543 out = []
544 xmit = data[0] >> 4
545 if xmit <= len(XMIT_TECH) - 1:
546 out.append("%s transmitter" % XMIT_TECH[xmit])
547 dev = data[0] & 0x0f
548 for b in range(4):
549 out.append(DEVICE_TECH[b][(dev >> (3 - b)) & 0x01])
550 self.annotate("Device technology", ', '.join(out))
551
552 def vendor(self, data):
553 name = bytes(data).strip().decode('ascii').strip('\x00')
554 if name:
555 self.annotate("Vendor", name)
556
557 def cdr(self, data):
558 out = []
559 value = data[0]
560 for b in range(8):
561 if value & 0x80:
562 out.append(CDR[b])
563 value <<= 1
564 self.annotate("CDR support", ', '.join(out))
565
566 def vendor_oui(self, data):
567 if data != [0, 0, 0]:
568 self.annotate("Vendor OUI", "%.2X-%.2X-%.2X" % tuple(data))
569
570 def vendor_pn(self, data):
571 name = bytes(data).strip().decode('ascii').strip('\x00')
572 if name:
573 self.annotate("Vendor part number", name)
574
575 def vendor_rev(self, data):
576 name = bytes(data).strip().decode('ascii').strip('\x00')
577 if name:
578 self.annotate("Vendor revision", name)
579
580 def wavelength(self, data):
581 value = (data[0] << 8) | data[1]
582 self.annotate("Wavelength", self.to_wavelength(value))
583
584 def wavelength_tolerance(self, data):
585 value = (data[0] << 8) | data[1]
586 self.annotate("Wavelength tolerance", self.to_wavelength_tolerance(value))
587
588 def max_case_temp(self, data):
589 self.annotate("Maximum case temperature", "%d C" % data[0])
590
591 def power_supply(self, data):
592 out = []
593 self.annotate("Max power dissipation",
594 "%.3f W" % (data[0] * 0.02), self.cnt - 3, self.cnt - 3)
595 self.annotate("Max power dissipation (powered down)",
596 "%.3f W" % (data[1] * 0.01), self.cnt - 2, self.cnt - 2)
597 value = (data[2] >> 4) * 0.050
598 self.annotate("Max current required (5V supply)",
599 "%.3f A" % value, self.cnt - 1, self.cnt - 1)
600 value = (data[2] & 0x0f) * 0.100
601 self.annotate("Max current required (3.3V supply)",
602 "%.3f A" % value, self.cnt - 1, self.cnt - 1)
603 value = (data[3] >> 4) * 0.100
604 self.annotate("Max current required (1.8V supply)",
605 "%.3f A" % value, self.cnt, self.cnt)
606 value = (data[3] & 0x0f) * 0.050
607 self.annotate("Max current required (-5.2V supply)",
608 "%.3f A" % value, self.cnt, self.cnt)
609
610 def vendor_sn(self, data):
611 name = bytes(data).strip().decode('ascii').strip('\x00')
612 if name:
613 self.annotate("Vendor serial number", name)
614
615 def manuf_date(self, data):
616 y = int(bytes(data[0:2])) + 2000
617 m = int(bytes(data[2:4]))
618 d = int(bytes(data[4:6]))
619 mnf = "%.4d-%.2d-%.2d" % (y, m, d)
620 lot = bytes(data[6:]).strip().decode('ascii').strip('\x00')
621 if lot:
622 mnf += " lot " + lot
623 self.annotate("Manufacturing date", mnf)
624
625 def diag_mon(self, data):
626 out = []
627 if data[0] & 0x10:
628 out.append("BER support")
629 else:
630 out.append("no BER support")
631 if data[0] & 0x08:
632 out.append("average power measurement")
633 else:
634 out.append("OMA power measurement")
635 self.annotate("Diagnostic monitoring", ', '.join(out))
636
637 def enhanced_opts(self, data):
638 out = []
639 value = data[0]
640 for b in range(8):
641 if value & 0x80:
642 out.append(ENHANCED_OPTS[b])
643 value <<= 1
644 self.annotate("Enhanced option support", ', '.join(out))
645
646 def aux_mon(self, data):
647 aux = AUX_TYPES[data[0] >> 4]
648 self.annotate("AUX1 monitoring", aux)
649 aux = AUX_TYPES[data[0] & 0x0f]
650 self.annotate("AUX2 monitoring", aux)
651