]> sigrok.org Git - libsigrokdecode.git/blob - decoders/xfp/pd.py
avr_isp: Add more parts
[libsigrokdecode.git] / decoders / xfp / pd.py
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
20 import sigrokdecode as srd
21 from common.plugtrx import (MODULE_ID, ALARM_THRESHOLDS, AD_READOUTS, GCS_BITS,
22         CONNECTOR, TRANSCEIVER, SERIAL_ENCODING, XMIT_TECH, CDR, DEVICE_TECH,
23         ENHANCED_OPTS, AUX_TYPES)
24
25 class Decoder(srd.Decoder):
26     api_version = 3
27     id = 'xfp'
28     name = 'XFP'
29     longname = '10 Gigabit Small Form Factor Pluggable Module (XFP)'
30     desc = 'XFP I²C management interface structures/protocol'
31     license = 'gplv3+'
32     inputs = ['i2c']
33     outputs = []
34     tags = ['Networking']
35     annotations = (
36         ('field-name-and-val', 'Field name and value'),
37         ('field-val', 'Field value'),
38     )
39     annotation_rows = (
40         ('field-names-and-vals', 'Field names and values', (0,)),
41         ('field-vals', 'Field values', (1,)),
42     )
43
44     def __init__(self):
45         self.reset()
46
47     def reset(self):
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
113     def start(self):
114         self.out_ann = self.register(srd.OUTPUT_ANN)
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
161     # representing 1/256C, to degrees Celsius.
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)