2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2015 Google, Inc
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 2 of the License, or
9 ## (at your option) any later version.
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.
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, write to the Free Software
18 ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 import sigrokdecode as srd
23 import zlib # for crc32
25 # BMC encoding with a 600kHz datarate
26 UI_US = 1000000/600000.0
28 # Threshold to discriminate half-1 from 0 in Binary Mark Conding
29 THRESHOLD_US = (UI_US + 2 * UI_US) / 2
31 # Control Message type
60 # 4b5b encoding of the symbols
71 0x01, # 1 = 0001 01001
72 0x04, # 4 = 0100 01010
73 0x05, # 5 = 0101 01011
76 0x06, # 6 = 0110 01110
77 0x07, # 7 = 0111 01111
80 0x08, # 8 = 1000 10010
81 0x09, # 9 = 1001 10011
82 0x02, # 2 = 0010 10100
83 0x03, # 3 = 0011 10101
84 0x0A, # A = 1010 10110
85 0x0B, # B = 1011 10111
88 0x0C, # C = 1100 11010
89 0x0D, # D = 1101 11011
90 0x0E, # E = 1110 11100
91 0x0F, # F = 1111 11101
92 0x00, # 0 = 0000 11110
102 SYNC_CODES = [SYNC1, SYNC2, SYNC3]
103 HRST_CODES = [RST1, RST1, RST1, RST2]
106 (SYNC1, SYNC1, SYNC1, SYNC2): 'SOP',
107 (SYNC1, SYNC1, SYNC3, SYNC3): "SOP'",
108 (SYNC1, SYNC3, SYNC1, SYNC3): 'SOP"',
109 (SYNC1, RST2, RST2, SYNC3): "SOP' Debug",
110 (SYNC1, RST2, SYNC3, SYNC2): 'SOP" Debug',
111 (RST1, SYNC1, RST1, SYNC3): 'Cable Reset',
112 (RST1, RST1, RST1, RST2): 'Hard Reset',
142 (1 << 24): 'no_suspend',
143 (1 << 25): 'comm_cap',
144 (1 << 26): 'cap_mismatch',
145 (1 << 27): 'give_back'
147 PDO_TYPE = ['', 'BATT:', 'VAR:', '<bad>']
149 (1 << 29): 'dual_role_power',
150 (1 << 28): 'suspend',
152 (1 << 26): 'comm_cap',
153 (1 << 25): 'dual_role_data'
174 # 16..31: SVID Specific Commands
175 # DisplayPort Commands
179 VDM_ACK = ['REQ', 'ACK', 'NAK', 'BSY']
181 class SamplerateError(Exception):
184 class Decoder(srd.Decoder):
186 id = 'usb_power_delivery'
188 longname = 'USB Power Delivery'
189 desc = 'USB Power Delivery protocol.'
194 {'id': 'cc', 'name': 'CC', 'desc': 'Control channel'},
197 {'id': 'fulltext', 'desc': 'full text decoding of the packet',
198 'default': 'no', 'values': ('yes', 'no')},
201 ('type', 'Packet Type'),
202 ('Preamble', 'Preamble'),
203 ('SOP', 'Start of Packet'),
207 ('EOP', 'End Of Packet'),
208 ('Sym', '4b5b symbols'),
209 ('warnings', 'Warnings'),
210 ('src', 'Source Message'),
211 ('snk', 'Sink Message'),
212 ('payload', 'Payload'),
213 ('text', 'Plain text'),
216 ('4B5B', 'symbols', (7, )),
217 ('Phase', 'parts', (1, 2, 3, 4, 5, 6, )),
218 ('payload', 'Payload', (11, )),
219 ('type', 'Type', (0, 9, 10, )),
220 ('warnings', 'Warnings', (8, )),
221 ('text', 'Full text', (12, )),
224 ('raw-data', 'RAW binary data'),
227 def get_request(self, rdo):
228 pos = (rdo >> 28) & 7
229 op_ma = ((rdo >> 10) & 0x3ff) * 10
230 max_ma = (rdo & 0x3ff) * 10
232 for f in RDO_FLAGS.keys():
234 flags += ' ' + RDO_FLAGS[f]
235 return '[%d]%d/%d mA%s' % (pos, op_ma, max_ma, flags)
237 def get_source_cap(self, pdo):
240 mv = ((pdo >> 10) & 0x3ff) * 50
241 ma = ((pdo >> 0) & 0x3ff) * 10
242 p = '%.1fV %.1fA' % (mv/1000.0, ma/1000.0)
244 minv = ((pdo >> 10) & 0x3ff) * 50
245 maxv = ((pdo >> 20) & 0x3ff) * 50
246 mw = ((pdo >> 0) & 0x3ff) * 250
247 p = '%.1f/%.1fV %.1fW' % (minv/1000.0, maxv/1000.0, mw/1000.0)
249 minv = ((pdo >> 10) & 0x3ff) * 50
250 maxv = ((pdo >> 20) & 0x3ff) * 50
251 ma = ((pdo >> 0) & 0x3ff) * 10
252 p = '%.1f/%.1fV %.1fA' % (minv/1000.0, maxv/1000.0, ma/1000.0)
256 for f in PDO_FLAGS.keys():
258 flags += ' ' + PDO_FLAGS[f]
259 return '%s%s%s' % (PDO_TYPE[t], p, flags)
261 def get_sink_cap(self, pdo):
264 mv = ((pdo >> 10) & 0x3ff) * 50
265 ma = ((pdo >> 0) & 0x3ff) * 10
266 p = '%.1fV %.1fA' % (mv/1000.0, ma/1000.0)
268 minv = ((pdo >> 10) & 0x3ff) * 50
269 maxv = ((pdo >> 20) & 0x3ff) * 50
270 mw = ((pdo >> 0) & 0x3ff) * 250
271 p = '%.1f/%.1fV %.1fW' % (minv/1000.0, maxv/1000.0, mw/1000.0)
273 minv = ((pdo >> 10) & 0x3ff) * 50
274 maxv = ((pdo >> 20) & 0x3ff) * 50
275 ma = ((pdo >> 0) & 0x3ff) * 10
276 p = '%.1f/%.1fV %.1fA' % (minv/1000.0, maxv/1000.0, ma/1000.0)
280 for f in PDO_FLAGS.keys():
282 flags += ' ' + PDO_FLAGS[f]
283 return '%s%s%s' % (PDO_TYPE[t], p, flags)
285 def get_vdm(self, idx, data):
286 if idx == 0: # VDM header
288 struct = data & (1 << 15)
290 if struct: # Structured VDM
292 src = data & (1 << 5)
293 ack = (data >> 6) & 3
294 pos = (data >> 8) & 7
295 ver = (data >> 13) & 3
296 txt = VDM_ACK[ack] + ' '
297 txt += VDM_CMDS[cmd] if cmd in VDM_CMDS else 'cmd?'
298 txt += ' pos %d' % (pos) if pos else ' '
299 else: # Unstructured VDM
300 txt = 'unstruct [%04x]' % (data & 0x7fff)
301 txt += ' SVID:%04x' % (vid)
303 txt = 'VDO:%08x' % (data)
306 def get_bist(self, idx, data):
308 counter = data & 0xffff
309 mode_name = BIST_MODES[mode] if mode in BIST_MODES else 'INVALID'
311 mode_name = 'Counter[= %d]' % (counter)
312 # TODO check all 0 bits are 0 / emit warnings
313 return 'mode %s' % (mode_name) if idx == 0 else 'invalid BRO'
315 def putpayload(self, s0, s1, idx):
319 txt = self.get_request(self.data[idx])
321 txt = self.get_source_cap(self.data[idx])
323 txt = self.get_sink_cap(self.data[idx])
325 txt = self.get_vdm(idx, self.data[idx])
327 txt = self.get_bist(idx, self.data[idx])
328 self.putx(s0, s1, [11, [txt, txt]])
329 self.text += ' - ' + txt
332 ann_type = 9 if self.head_power_role() else 10
333 role = 'SRC' if self.head_power_role() else 'SNK'
334 if self.head_data_role() != self.head_power_role():
335 role += '/DFP' if self.head_data_role() else '/UFP'
337 if self.head_count() == 0:
338 shortm = CTRL_TYPES[t]
340 shortm = DATA_TYPES[t] if t in DATA_TYPES else 'DAT???'
342 longm = '{:s}[{:d}]:{:s}'.format(role, self.head_id(), shortm)
343 self.putx(0, -1, [ann_type, [longm, shortm]])
347 return (self.head >> 9) & 7
349 def head_power_role(self):
350 return (self.head >> 8) & 1
352 def head_data_role(self):
353 return (self.head >> 5) & 1
356 return ((self.head >> 6) & 3) + 1
359 return self.head & 0xF
361 def head_count(self):
362 return (self.head >> 12) & 7
364 def putx(self, s0, s1, data):
365 self.put(self.edges[s0], self.edges[s1], self.out_ann, data)
367 def putwarn(self, longm, shortm):
368 self.putx(0, -1, [8, [longm, shortm]])
370 def compute_crc32(self):
371 bdata = struct.pack('<H'+'I'*len(self.data), self.head & 0xffff,
372 *tuple([d & 0xffffffff for d in self.data]))
373 return zlib.crc32(bdata)
375 def rec_sym(self, i, sym):
376 self.putx(i, i+5, [7, SYM_NAME[sym]])
378 def get_sym(self, i, rec=True):
379 v = (self.bits[i] | (self.bits[i+1] << 1) | (self.bits[i+2] << 2) |
380 (self.bits[i+3] << 3) | (self.bits[i+4] << 4))
388 # Check it's not a truncated packet
389 if len(self.bits) - i <= 20:
390 self.putwarn('Truncated', '!')
392 k = [self.get_sym(i), self.get_sym(i+5),
393 self.get_sym(i+10), self.get_sym(i+15)]
394 # TODO check bad symbols
395 val = k[0] | (k[1] << 4) | (k[2] << 8) | (k[3] << 12)
400 lo = self.get_short()
401 hi = self.get_short()
402 return lo | (hi << 16)
404 def find_corrupted_sop(self, k):
405 # Start of packet are valid even if they have only 3 correct symbols
407 for seq in START_OF_PACKETS.keys():
408 if [k[i] == seq[i] for i in range(len(k))].count(True) >= 3:
409 return START_OF_PACKETS[seq]
413 for i in range(len(self.bits) - 19):
414 k = (self.get_sym(i, rec=False), self.get_sym(i+5, rec=False),
415 self.get_sym(i+10, rec=False), self.get_sym(i+15, rec=False))
416 sym = START_OF_PACKETS[k] if k in START_OF_PACKETS else None
418 sym = self.find_corrupted_sop(k)
419 # We have an interesting symbol sequence
421 # annotate the preamble
422 self.putx(0, i, [1, ['Preamble', '...']])
423 # annotate each symbol
424 self.rec_sym(i, k[0])
425 self.rec_sym(i+5, k[1])
426 self.rec_sym(i+10, k[2])
427 self.rec_sym(i+15, k[3])
428 if sym == 'Hard Reset':
430 return -1 # Hard reset
431 elif sym == 'Cable Reset':
433 return -1 # Cable reset
435 self.putx(i, i+20, [2, [sym, 'S']])
437 self.putx(0, len(self.bits), [1, ['Junk???', 'XXX']])
438 self.text += 'Junk???'
439 self.putwarn('No start of packet found', 'XXX')
440 return -1 # No Start Of Packet
443 self.samplerate = None
449 self.startsample = None
453 self.half_one = False
456 def metadata(self, key, value):
457 if key == srd.SRD_CONF_SAMPLERATE:
458 self.samplerate = value
459 # 0 is 2 UI, space larger than 1.5x 0 is definitely wrong
460 self.maxbit = self.us2samples(3 * UI_US)
461 # duration threshold between half 1 and 0
462 self.threshold = self.us2samples(THRESHOLD_US)
465 self.out_python = self.register(srd.OUTPUT_PYTHON)
466 self.out_ann = self.register(srd.OUTPUT_ANN)
467 self.out_binary = self.register(srd.OUTPUT_BINARY)
468 self.out_bitrate = self.register(
470 meta=(int, 'Bitrate', 'Bitrate during the packet')
473 def us2samples(self, us):
474 if not self.samplerate:
475 raise SamplerateError('Need the samplerate.')
476 return int(us * self.samplerate / 1000000)
478 def decode_packet(self):
483 if len(self.edges) < 50:
484 return # Not a real PD packet
487 tstamp = float(self.startsample) / self.samplerate
488 self.text += '#%-4d (%8.6fms): ' % (self.packet_seq, tstamp*1000)
490 self.idx = self.scan_eop()
492 # Full text trace of the issue
493 self.putx(0, self.idx, [12, [self.text, '...']])
494 return # No real packet: ABORT
497 self.head = self.get_short()
498 self.putx(self.idx-20, self.idx, [3, ['H:%04x' % (self.head), 'HD']])
501 # Decode data payload
502 for i in range(self.head_count()):
503 self.data.append(self.get_word())
504 self.putx(self.idx-40, self.idx,
505 [4, ['[%d]%08x' % (i, self.data[i]), 'D%d' % (i)]])
506 self.putpayload(self.idx-40, self.idx, i)
509 self.crc = self.get_word()
510 ccrc = self.compute_crc32()
512 self.putwarn('Bad CRC %08x != %08x' % (self.crc, ccrc), 'CRC!')
513 self.putx(self.idx-40, self.idx, [5, ['CRC:%08x' % (self.crc), 'CRC']])
516 if len(self.bits) >= self.idx + 5 and self.get_sym(self.idx) == EOP:
517 self.putx(self.idx, self.idx + 5, [6, ['EOP', 'E']])
520 self.putwarn('No EOP', 'EOP!')
522 if self.options['fulltext'] == 'yes':
523 self.putx(0, self.idx, [12, [self.text, '...']])
525 # Meta data for bitrate
526 ss, es = self.edges[0], self.edges[-1]
527 bitrate = self.samplerate*len(self.bits) / float(es - ss)
528 self.put(es, ss, self.out_bitrate, int(bitrate))
529 # Raw binary data (BMC decoded)
530 self.put(es, ss, self.out_binary, [0, bytes(self.bits)])
532 def decode(self, ss, es, data):
533 if not self.samplerate:
534 raise SamplerateError('Cannot decode without samplerate.')
535 for (self.samplenum, pins) in data:
537 if self.oldpins == pins:
540 self.oldpins, (cc, ) = pins, pins
542 # First sample of the packet, just record the start date
543 if not self.startsample:
544 self.startsample = self.samplenum
545 self.previous = self.samplenum
548 diff = self.samplenum - self.previous
550 # Large idle: use it as the end of packet
551 if diff > self.maxbit:
552 # the last edge of the packet
553 self.edges.append(self.previous)
556 # Reset for next packet
557 self.startsample = self.samplenum
561 self.half_one = False
563 else: # add the bit to the packet
564 is_zero = diff > self.threshold
565 if is_zero and not self.half_one:
567 self.edges.append(self.previous)
568 elif not is_zero and self.half_one:
570 self.edges.append(self.start_one)
571 self.half_one = False
572 elif not is_zero and not self.half_one:
574 self.start_one = self.previous
575 else: # Invalid BMC sequence
576 self.bad.append((self.start_one, self.previous))
577 # TODO try to recover
579 self.edges.append(self.previous)
580 self.half_one = False
581 self.previous = self.samplenum