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