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, see <http://www.gnu.org/licenses/>.
20 import sigrokdecode as srd
22 import zlib # for crc32
24 # BMC encoding with a 600kHz datarate
25 UI_US = 1000000/600000.0
27 # Threshold to discriminate half-1 from 0 in Binary Mark Conding
28 THRESHOLD_US = (UI_US + 2 * UI_US) / 2
30 # Control Message type
59 # 4b5b encoding of the symbols
70 0x01, # 1 = 0001 01001
71 0x04, # 4 = 0100 01010
72 0x05, # 5 = 0101 01011
75 0x06, # 6 = 0110 01110
76 0x07, # 7 = 0111 01111
79 0x08, # 8 = 1000 10010
80 0x09, # 9 = 1001 10011
81 0x02, # 2 = 0010 10100
82 0x03, # 3 = 0011 10101
83 0x0A, # A = 1010 10110
84 0x0B, # B = 1011 10111
87 0x0C, # C = 1100 11010
88 0x0D, # D = 1101 11011
89 0x0E, # E = 1110 11100
90 0x0F, # F = 1111 11101
91 0x00, # 0 = 0000 11110
101 SYNC_CODES = [SYNC1, SYNC2, SYNC3]
102 HRST_CODES = [RST1, RST1, RST1, RST2]
105 (SYNC1, SYNC1, SYNC1, SYNC2),
106 (SYNC1, SYNC1, SYNC3, SYNC3),
107 (SYNC1, SYNC3, SYNC1, SYNC3),
108 (SYNC1, RST2, RST2, SYNC3),
109 (SYNC1, RST2, SYNC3, SYNC2),
110 (RST1, SYNC1, RST1, SYNC3),
111 (RST1, RST1, RST1, RST2),
114 SOP_SEQUENCES[0]: 'SOP',
115 SOP_SEQUENCES[1]: "SOP'",
116 SOP_SEQUENCES[2]: 'SOP"',
117 SOP_SEQUENCES[3]: "SOP' Debug",
118 SOP_SEQUENCES[4]: 'SOP" Debug',
119 SOP_SEQUENCES[5]: 'Cable Reset',
120 SOP_SEQUENCES[6]: 'Hard Reset',
150 (1 << 24): 'no_suspend',
151 (1 << 25): 'comm_cap',
152 (1 << 26): 'cap_mismatch',
153 (1 << 27): 'give_back'
155 PDO_TYPE = ['', 'BATT:', 'VAR:', '<bad>']
157 (1 << 29): 'dual_role_power',
158 (1 << 28): 'suspend',
160 (1 << 26): 'comm_cap',
161 (1 << 25): 'dual_role_data'
182 # 16..31: SVID Specific Commands
183 # DisplayPort Commands
187 VDM_ACK = ['REQ', 'ACK', 'NAK', 'BSY']
189 class SamplerateError(Exception):
192 class Decoder(srd.Decoder):
194 id = 'usb_power_delivery'
196 longname = 'USB Power Delivery'
197 desc = 'USB Power Delivery protocol.'
202 {'id': 'cc', 'name': 'CC', 'desc': 'Control channel'},
205 {'id': 'fulltext', 'desc': 'full text decoding of the packet',
206 'default': 'no', 'values': ('yes', 'no')},
209 ('type', 'Packet Type'),
210 ('Preamble', 'Preamble'),
211 ('SOP', 'Start of Packet'),
215 ('EOP', 'End Of Packet'),
216 ('Sym', '4b5b symbols'),
217 ('warnings', 'Warnings'),
218 ('src', 'Source Message'),
219 ('snk', 'Sink Message'),
220 ('payload', 'Payload'),
221 ('text', 'Plain text'),
224 ('4B5B', 'symbols', (7, )),
225 ('Phase', 'parts', (1, 2, 3, 4, 5, 6, )),
226 ('payload', 'Payload', (11, )),
227 ('type', 'Type', (0, 9, 10, )),
228 ('warnings', 'Warnings', (8, )),
229 ('text', 'Full text', (12, )),
232 ('raw-data', 'RAW binary data'),
235 def get_request(self, rdo):
236 pos = (rdo >> 28) & 7
237 op_ma = ((rdo >> 10) & 0x3ff) * 10
238 max_ma = (rdo & 0x3ff) * 10
240 for f in sorted(RDO_FLAGS.keys(), reverse = True):
242 flags += ' ' + RDO_FLAGS[f]
243 return '[%d]%d/%d mA%s' % (pos, op_ma, max_ma, flags)
245 def get_source_cap(self, pdo):
248 mv = ((pdo >> 10) & 0x3ff) * 50
249 ma = ((pdo >> 0) & 0x3ff) * 10
250 p = '%.1fV %.1fA' % (mv/1000.0, ma/1000.0)
252 minv = ((pdo >> 10) & 0x3ff) * 50
253 maxv = ((pdo >> 20) & 0x3ff) * 50
254 mw = ((pdo >> 0) & 0x3ff) * 250
255 p = '%.1f/%.1fV %.1fW' % (minv/1000.0, maxv/1000.0, mw/1000.0)
257 minv = ((pdo >> 10) & 0x3ff) * 50
258 maxv = ((pdo >> 20) & 0x3ff) * 50
259 ma = ((pdo >> 0) & 0x3ff) * 10
260 p = '%.1f/%.1fV %.1fA' % (minv/1000.0, maxv/1000.0, ma/1000.0)
264 for f in sorted(PDO_FLAGS.keys(), reverse = True):
266 flags += ' ' + PDO_FLAGS[f]
267 return '%s%s%s' % (PDO_TYPE[t], p, flags)
269 def get_sink_cap(self, pdo):
272 mv = ((pdo >> 10) & 0x3ff) * 50
273 ma = ((pdo >> 0) & 0x3ff) * 10
274 p = '%.1fV %.1fA' % (mv/1000.0, ma/1000.0)
276 minv = ((pdo >> 10) & 0x3ff) * 50
277 maxv = ((pdo >> 20) & 0x3ff) * 50
278 mw = ((pdo >> 0) & 0x3ff) * 250
279 p = '%.1f/%.1fV %.1fW' % (minv/1000.0, maxv/1000.0, mw/1000.0)
281 minv = ((pdo >> 10) & 0x3ff) * 50
282 maxv = ((pdo >> 20) & 0x3ff) * 50
283 ma = ((pdo >> 0) & 0x3ff) * 10
284 p = '%.1f/%.1fV %.1fA' % (minv/1000.0, maxv/1000.0, ma/1000.0)
288 for f in sorted(PDO_FLAGS.keys(), reverse = True):
290 flags += ' ' + PDO_FLAGS[f]
291 return '%s%s%s' % (PDO_TYPE[t], p, flags)
293 def get_vdm(self, idx, data):
294 if idx == 0: # VDM header
296 struct = data & (1 << 15)
298 if struct: # Structured VDM
300 src = data & (1 << 5)
301 ack = (data >> 6) & 3
302 pos = (data >> 8) & 7
303 ver = (data >> 13) & 3
304 txt = VDM_ACK[ack] + ' '
305 txt += VDM_CMDS[cmd] if cmd in VDM_CMDS else 'cmd?'
306 txt += ' pos %d' % (pos) if pos else ' '
307 else: # Unstructured VDM
308 txt = 'unstruct [%04x]' % (data & 0x7fff)
309 txt += ' SVID:%04x' % (vid)
311 txt = 'VDO:%08x' % (data)
314 def get_bist(self, idx, data):
316 counter = data & 0xffff
317 mode_name = BIST_MODES[mode] if mode in BIST_MODES else 'INVALID'
319 mode_name = 'Counter[= %d]' % (counter)
320 # TODO check all 0 bits are 0 / emit warnings
321 return 'mode %s' % (mode_name) if idx == 0 else 'invalid BRO'
323 def putpayload(self, s0, s1, idx):
327 txt = self.get_request(self.data[idx])
329 txt = self.get_source_cap(self.data[idx])
331 txt = self.get_sink_cap(self.data[idx])
333 txt = self.get_vdm(idx, self.data[idx])
335 txt = self.get_bist(idx, self.data[idx])
336 self.putx(s0, s1, [11, [txt, txt]])
337 self.text += ' - ' + txt
340 ann_type = 9 if self.head_power_role() else 10
341 role = 'SRC' if self.head_power_role() else 'SNK'
342 if self.head_data_role() != self.head_power_role():
343 role += '/DFP' if self.head_data_role() else '/UFP'
345 if self.head_count() == 0:
346 shortm = CTRL_TYPES[t]
348 shortm = DATA_TYPES[t] if t in DATA_TYPES else 'DAT???'
350 longm = '{:s}[{:d}]:{:s}'.format(role, self.head_id(), shortm)
351 self.putx(0, -1, [ann_type, [longm, shortm]])
355 return (self.head >> 9) & 7
357 def head_power_role(self):
358 return (self.head >> 8) & 1
360 def head_data_role(self):
361 return (self.head >> 5) & 1
364 return ((self.head >> 6) & 3) + 1
367 return self.head & 0xF
369 def head_count(self):
370 return (self.head >> 12) & 7
372 def putx(self, s0, s1, data):
373 self.put(self.edges[s0], self.edges[s1], self.out_ann, data)
375 def putwarn(self, longm, shortm):
376 self.putx(0, -1, [8, [longm, shortm]])
378 def compute_crc32(self):
379 bdata = struct.pack('<H'+'I'*len(self.data), self.head & 0xffff,
380 *tuple([d & 0xffffffff for d in self.data]))
381 return zlib.crc32(bdata)
383 def rec_sym(self, i, sym):
384 self.putx(i, i+5, [7, SYM_NAME[sym]])
386 def get_sym(self, i, rec=True):
387 v = (self.bits[i] | (self.bits[i+1] << 1) | (self.bits[i+2] << 2) |
388 (self.bits[i+3] << 3) | (self.bits[i+4] << 4))
396 # Check it's not a truncated packet
397 if len(self.bits) - i <= 20:
398 self.putwarn('Truncated', '!')
400 k = [self.get_sym(i), self.get_sym(i+5),
401 self.get_sym(i+10), self.get_sym(i+15)]
402 # TODO check bad symbols
403 val = k[0] | (k[1] << 4) | (k[2] << 8) | (k[3] << 12)
408 lo = self.get_short()
409 hi = self.get_short()
410 return lo | (hi << 16)
412 def find_corrupted_sop(self, k):
413 # Start of packet are valid even if they have only 3 correct symbols
415 for seq in SOP_SEQUENCES:
416 if [k[i] == seq[i] for i in range(len(k))].count(True) >= 3:
417 return START_OF_PACKETS[seq]
421 for i in range(len(self.bits) - 19):
422 k = (self.get_sym(i, rec=False), self.get_sym(i+5, rec=False),
423 self.get_sym(i+10, rec=False), self.get_sym(i+15, rec=False))
424 sym = START_OF_PACKETS.get(k, None)
426 sym = self.find_corrupted_sop(k)
427 # We have an interesting symbol sequence
429 # annotate the preamble
430 self.putx(0, i, [1, ['Preamble', '...']])
431 # annotate each symbol
432 self.rec_sym(i, k[0])
433 self.rec_sym(i+5, k[1])
434 self.rec_sym(i+10, k[2])
435 self.rec_sym(i+15, k[3])
436 if sym == 'Hard Reset':
438 return -1 # Hard reset
439 elif sym == 'Cable Reset':
441 return -1 # Cable reset
443 self.putx(i, i+20, [2, [sym, 'S']])
445 self.putx(0, len(self.bits), [1, ['Junk???', 'XXX']])
446 self.text += 'Junk???'
447 self.putwarn('No start of packet found', 'XXX')
448 return -1 # No Start Of Packet
454 self.samplerate = None
458 self.startsample = None
462 self.half_one = False
465 def metadata(self, key, value):
466 if key == srd.SRD_CONF_SAMPLERATE:
467 self.samplerate = value
468 # 0 is 2 UI, space larger than 1.5x 0 is definitely wrong
469 self.maxbit = self.us2samples(3 * UI_US)
470 # duration threshold between half 1 and 0
471 self.threshold = self.us2samples(THRESHOLD_US)
474 self.out_python = self.register(srd.OUTPUT_PYTHON)
475 self.out_ann = self.register(srd.OUTPUT_ANN)
476 self.out_binary = self.register(srd.OUTPUT_BINARY)
477 self.out_bitrate = self.register(
479 meta=(int, 'Bitrate', 'Bitrate during the packet')
482 def us2samples(self, us):
483 return int(us * self.samplerate / 1000000)
485 def decode_packet(self):
490 if len(self.edges) < 50:
491 return # Not a real PD packet
494 tstamp = float(self.startsample) / self.samplerate
495 self.text += '#%-4d (%8.6fms): ' % (self.packet_seq, tstamp*1000)
497 self.idx = self.scan_eop()
499 # Full text trace of the issue
500 self.putx(0, self.idx, [12, [self.text, '...']])
501 return # No real packet: ABORT
504 self.head = self.get_short()
505 self.putx(self.idx-20, self.idx, [3, ['H:%04x' % (self.head), 'HD']])
508 # Decode data payload
509 for i in range(self.head_count()):
510 self.data.append(self.get_word())
511 self.putx(self.idx-40, self.idx,
512 [4, ['[%d]%08x' % (i, self.data[i]), 'D%d' % (i)]])
513 self.putpayload(self.idx-40, self.idx, i)
516 self.crc = self.get_word()
517 ccrc = self.compute_crc32()
519 self.putwarn('Bad CRC %08x != %08x' % (self.crc, ccrc), 'CRC!')
520 self.putx(self.idx-40, self.idx, [5, ['CRC:%08x' % (self.crc), 'CRC']])
523 if len(self.bits) >= self.idx + 5 and self.get_sym(self.idx) == EOP:
524 self.putx(self.idx, self.idx + 5, [6, ['EOP', 'E']])
527 self.putwarn('No EOP', 'EOP!')
529 if self.options['fulltext'] == 'yes':
530 self.putx(0, self.idx, [12, [self.text, '...']])
532 # Meta data for bitrate
533 ss, es = self.edges[0], self.edges[-1]
534 bitrate = self.samplerate*len(self.bits) / float(es - ss)
535 self.put(es, ss, self.out_bitrate, int(bitrate))
536 # Raw binary data (BMC decoded)
537 self.put(es, ss, self.out_binary, [0, bytes(self.bits)])
540 if not self.samplerate:
541 raise SamplerateError('Cannot decode without samplerate.')
545 # First sample of the packet, just record the start date
546 if not self.startsample:
547 self.startsample = self.samplenum
548 self.previous = self.samplenum
551 diff = self.samplenum - self.previous
553 # Large idle: use it as the end of packet
554 if diff > self.maxbit:
555 # the last edge of the packet
556 self.edges.append(self.previous)
559 # Reset for next packet
560 self.startsample = self.samplenum
564 self.half_one = False
566 else: # add the bit to the packet
567 is_zero = diff > self.threshold
568 if is_zero and not self.half_one:
570 self.edges.append(self.previous)
571 elif not is_zero and self.half_one:
573 self.edges.append(self.start_one)
574 self.half_one = False
575 elif not is_zero and not self.half_one:
577 self.start_one = self.previous
578 else: # Invalid BMC sequence
579 self.bad.append((self.start_one, self.previous))
580 # TODO try to recover
582 self.edges.append(self.previous)
583 self.half_one = False
584 self.previous = self.samplenum