2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2015 Google, Inc
5 ## Copyright (C) 2018 davidanger <davidanger@163.com>
6 ## Copyright (C) 2018 Peter Hazenberg <sigrok@haas-en-berg.nl>
8 ## This program is free software; you can redistribute it and/or modify
9 ## it under the terms of the GNU General Public License as published by
10 ## the Free Software Foundation; either version 2 of the License, or
11 ## (at your option) any later version.
13 ## This program is distributed in the hope that it will be useful,
14 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ## GNU General Public License for more details.
18 ## You should have received a copy of the GNU General Public License
19 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
22 import sigrokdecode as srd
24 import zlib # for crc32
26 # BMC encoding with a 600kHz datarate
27 UI_US = 1000000/600000.0
29 # Threshold to discriminate half-1 from 0 in Binary Mark Conding
30 THRESHOLD_US = (UI_US + 2 * UI_US) / 2
32 # Control Message type
51 17: 'Get_Source_Cap_Extended',
55 21: 'Get_Country_Codes',
66 7: 'Get_Country_Info',
70 # 4b5b encoding of the symbols
81 0x01, # 1 = 0001 01001
82 0x04, # 4 = 0100 01010
83 0x05, # 5 = 0101 01011
86 0x06, # 6 = 0110 01110
87 0x07, # 7 = 0111 01111
90 0x08, # 8 = 1000 10010
91 0x09, # 9 = 1001 10011
92 0x02, # 2 = 0010 10100
93 0x03, # 3 = 0011 10101
94 0x0A, # A = 1010 10110
95 0x0B, # B = 1011 10111
98 0x0C, # C = 1100 11010
99 0x0D, # D = 1101 11011
100 0x0E, # E = 1110 11100
101 0x0F, # F = 1111 11101
102 0x00, # 0 = 0000 11110
112 SYNC_CODES = [SYNC1, SYNC2, SYNC3]
113 HRST_CODES = [RST1, RST1, RST1, RST2]
116 (SYNC1, SYNC1, SYNC1, SYNC2),
117 (SYNC1, SYNC1, SYNC3, SYNC3),
118 (SYNC1, SYNC3, SYNC1, SYNC3),
119 (SYNC1, RST2, RST2, SYNC3),
120 (SYNC1, RST2, SYNC3, SYNC2),
121 (RST1, SYNC1, RST1, SYNC3),
122 (RST1, RST1, RST1, RST2),
125 SOP_SEQUENCES[0]: 'SOP',
126 SOP_SEQUENCES[1]: "SOP'",
127 SOP_SEQUENCES[2]: 'SOP"',
128 SOP_SEQUENCES[3]: "SOP' Debug",
129 SOP_SEQUENCES[4]: 'SOP" Debug',
130 SOP_SEQUENCES[5]: 'Cable Reset',
131 SOP_SEQUENCES[6]: 'Hard Reset',
161 (1 << 23): 'unchunked',
162 (1 << 24): 'no_suspend',
163 (1 << 25): 'comm_cap',
164 (1 << 26): 'cap_mismatch',
165 (1 << 27): 'give_back'
186 # 16..31: SVID Specific Commands
187 # DisplayPort Commands
191 VDM_ACK = ['REQ', 'ACK', 'NAK', 'BSY']
194 class SamplerateError(Exception):
197 class Decoder(srd.Decoder):
199 id = 'usb_power_delivery'
201 longname = 'USB Power Delivery'
202 desc = 'USB Power Delivery protocol.'
208 {'id': 'cc1', 'name': 'CC1', 'desc': 'Configuration Channel 1'},
210 optional_channels = (
211 {'id': 'cc2', 'name': 'CC2', 'desc': 'Configuration Channel 2'},
214 {'id': 'fulltext', 'desc': 'Full text decoding of packets',
215 'default': 'no', 'values': ('yes', 'no')},
218 ('type', 'Packet Type'),
219 ('preamble', 'Preamble'),
220 ('sop', 'Start of Packet'),
221 ('header', 'Header'),
224 ('eop', 'End Of Packet'),
225 ('sym', '4b5b symbols'),
226 ('warning', 'Warning'),
227 ('src', 'Source Message'),
228 ('snk', 'Sink Message'),
229 ('payload', 'Payload'),
230 ('text', 'Plain text'),
233 ('4b5b', 'Symbols', (7,)),
234 ('parts', 'Parts', (1, 2, 3, 4, 5, 6)),
235 ('payloads', 'Payloads', (11,)),
236 ('types', 'Types', (0, 9, 10)),
237 ('warnings', 'Warnings', (8,)),
238 ('texts', 'Full text', (12,)),
241 ('raw-data', 'RAW binary data'),
246 def get_request(self, rdo):
247 pos = (rdo >> 28) & 7
249 op_ma = ((rdo >> 10) & 0x3ff) * 0.01
250 max_ma = (rdo & 0x3ff) * 0.01
252 mark = self.cap_mark[pos]
254 op_v = ((rdo >> 9) & 0x7ff) * 0.02
255 op_a = (rdo & 0x3f) * 0.05
256 t_settings = '%gV %gA' % (op_v, op_a)
258 op_w = ((rdo >> 10) & 0x3ff) * 0.25
259 mp_w = (rdo & 0x3ff) * 0.25
260 t_settings = '%gW (operating)' % op_w
262 op_a = ((rdo >> 10) & 0x3ff) * 0.01
263 max_a = (rdo & 0x3ff) * 0.01
264 t_settings = '%gA (operating) / %gA (max)' % (op_a, max_a)
267 for f in sorted(RDO_FLAGS.keys(), reverse = True):
269 t_flags += ' [' + RDO_FLAGS[f] + ']'
271 if pos in self.stored_pdos.keys():
272 t_pdo = '#%d: %s' % (pos, self.stored_pdos[pos])
274 t_pdo = '#%d' % (pos)
276 return '(PDO %s) %s%s' % (t_pdo, t_settings, t_flags)
278 def get_source_sink_cap(self, pdo, idx, source):
280 self.cap_mark[idx] = t1
287 (1 << 29): 'dual_role_power',
288 (1 << 28): 'suspend',
289 (1 << 27): 'unconstrained',
290 (1 << 26): 'comm_cap',
291 (1 << 25): 'dual_role_data',
292 (1 << 24): 'unchunked',
296 (1 << 29): 'dual_role_power',
297 (1 << 28): 'high_capability',
298 (1 << 27): 'unconstrained',
299 (1 << 26): 'comm_cap',
300 (1 << 25): 'dual_role_data',
301 (0b01 << 23): 'fr_swap default power',
302 (0b10 << 23): 'fr_swap 1.5 A',
303 (0b11 << 23): 'fr_swap 3.0 A',
305 mv = ((pdo >> 10) & 0x3ff) * 0.05
306 ma = ((pdo >> 0) & 0x3ff) * 0.01
307 p = '%gV %gA (%gW)' % (mv, ma, mv*ma)
308 self.stored_pdos[idx] = '%s %gV' % (t_name, mv)
311 flags = {} # No flags defined for Battery PDO in PD 3.0 spec
312 minv = ((pdo >> 10) & 0x3ff) * 0.05
313 maxv = ((pdo >> 20) & 0x3ff) * 0.05
314 mw = ((pdo >> 0) & 0x3ff) * 0.25
315 p = '%g/%gV %gW' % (minv, maxv, mw)
316 self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv)
319 flags = {} # No flags defined for Variable PDO in PD 3.0 spec
320 minv = ((pdo >> 10) & 0x3ff) * 0.05
321 maxv = ((pdo >> 20) & 0x3ff) * 0.05
322 ma = ((pdo >> 0) & 0x3ff) * 0.01
323 p = '%g/%gV %gA' % (minv, maxv, ma)
324 self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv)
328 t_name = 'Programmable|PPS'
330 (1 << 29): 'power_limited',
332 minv = ((pdo >> 8) & 0xff) * 0.1
333 maxv = ((pdo >> 17) & 0xff) * 0.1
334 ma = ((pdo >> 0) & 0xff) * 0.05
335 p = '%g/%gV %gA' % (minv, maxv, ma)
336 if (pdo >> 27) & 0x1:
338 self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv)
340 t_name = 'Reserved APDO: '+bin(t2)
341 p = '[raw: %s]' % (bin(pdo))
342 self.stored_pdos[idx] = '%s %s' % (t_name, p)
344 for f in sorted(flags.keys(), reverse = True):
346 t_flags += ' [' + flags[f] + ']'
347 return '[%s] %s%s' % (t_name, p, t_flags)
349 def get_vdm(self, idx, data):
350 if idx == 0: # VDM header
352 struct = data & (1 << 15)
354 if struct: # Structured VDM
356 src = data & (1 << 5)
357 ack = (data >> 6) & 3
358 pos = (data >> 8) & 7
359 ver = (data >> 13) & 3
360 txt = VDM_ACK[ack] + ' '
361 txt += VDM_CMDS[cmd] if cmd in VDM_CMDS else 'cmd?'
362 txt += ' pos %d' % (pos) if pos else ' '
363 else: # Unstructured VDM
364 txt = 'unstruct [%04x]' % (data & 0x7fff)
365 txt += ' SVID:%04x' % (vid)
367 txt = 'VDO:%08x' % (data)
370 def get_bist(self, idx, data):
372 counter = data & 0xffff
373 mode_name = BIST_MODES[mode] if mode in BIST_MODES else 'INVALID'
375 mode_name = 'Counter[= %d]' % (counter)
376 # TODO: Check all 0 bits are 0 / emit warnings.
377 return 'mode %s' % (mode_name) if idx == 0 else 'invalid BRO'
379 def putpayload(self, s0, s1, idx):
381 txt = '['+str(idx+1)+'] '
383 txt += self.get_request(self.data[idx])
384 elif t == 1 or t == 4:
385 txt += self.get_source_sink_cap(self.data[idx], idx+1, t==1)
387 txt += self.get_vdm(idx, self.data[idx])
389 txt += self.get_bist(idx, self.data[idx])
390 self.putx(s0, s1, [11, [txt, txt]])
391 self.text += ' - ' + txt
394 ann_type = 9 if self.head_power_role() else 10
395 role = 'SRC' if self.head_power_role() else 'SNK'
396 if self.head_data_role() != self.head_power_role():
397 role += '/DFP' if self.head_data_role() else '/UFP'
399 if self.head_count() == 0:
400 shortm = CTRL_TYPES[t]
402 shortm = DATA_TYPES[t] if t in DATA_TYPES else 'DAT???'
404 longm = '(r{:d}) {:s}[{:d}]: {:s}'.format(self.head_rev(), role, self.head_id(), shortm)
405 self.putx(0, -1, [ann_type, [longm, shortm]])
409 return (self.head >> 9) & 7
411 def head_power_role(self):
412 return (self.head >> 8) & 1
414 def head_data_role(self):
415 return (self.head >> 5) & 1
418 return ((self.head >> 6) & 3) + 1
421 return self.head & 0xF
423 def head_count(self):
424 return (self.head >> 12) & 7
426 def putx(self, s0, s1, data):
427 self.put(self.edges[s0], self.edges[s1], self.out_ann, data)
429 def putwarn(self, longm, shortm):
430 self.putx(0, -1, [8, [longm, shortm]])
432 def compute_crc32(self):
433 bdata = struct.pack('<H'+'I'*len(self.data), self.head & 0xffff,
434 *tuple([d & 0xffffffff for d in self.data]))
435 return zlib.crc32(bdata)
437 def rec_sym(self, i, sym):
438 self.putx(i, i+5, [7, SYM_NAME[sym]])
440 def get_sym(self, i, rec=True):
441 v = (self.bits[i] | (self.bits[i+1] << 1) | (self.bits[i+2] << 2) |
442 (self.bits[i+3] << 3) | (self.bits[i+4] << 4))
450 # Check it's not a truncated packet.
451 if len(self.bits) - i <= 20:
452 self.putwarn('Truncated', '!')
454 k = [self.get_sym(i), self.get_sym(i+5),
455 self.get_sym(i+10), self.get_sym(i+15)]
456 # TODO: Check bad symbols.
457 val = k[0] | (k[1] << 4) | (k[2] << 8) | (k[3] << 12)
462 lo = self.get_short()
463 hi = self.get_short()
464 return lo | (hi << 16)
466 def find_corrupted_sop(self, k):
467 # Start of packet are valid even if they have only 3 correct symbols
469 for seq in SOP_SEQUENCES:
470 if [k[i] == seq[i] for i in range(len(k))].count(True) >= 3:
471 return START_OF_PACKETS[seq]
475 for i in range(len(self.bits) - 19):
476 k = (self.get_sym(i, rec=False), self.get_sym(i+5, rec=False),
477 self.get_sym(i+10, rec=False), self.get_sym(i+15, rec=False))
478 sym = START_OF_PACKETS.get(k, None)
480 sym = self.find_corrupted_sop(k)
481 # We have an interesting symbol sequence.
483 # Annotate the preamble.
484 self.putx(0, i, [1, ['Preamble', '...']])
485 # Annotate each symbol.
486 self.rec_sym(i, k[0])
487 self.rec_sym(i+5, k[1])
488 self.rec_sym(i+10, k[2])
489 self.rec_sym(i+15, k[3])
490 if sym == 'Hard Reset':
492 return -1 # Hard reset
493 elif sym == 'Cable Reset':
495 return -1 # Cable reset
497 self.putx(i, i+20, [2, [sym, 'S']])
499 self.putx(0, len(self.bits), [1, ['Junk???', 'XXX']])
500 self.text += 'Junk???'
501 self.putwarn('No start of packet found', 'XXX')
502 return -1 # No Start Of Packet
508 self.samplerate = None
512 self.startsample = None
516 self.half_one = False
518 self.stored_pdos = {}
519 self.cap_mark = [0, 0, 0, 0, 0, 0, 0, 0]
521 def metadata(self, key, value):
522 if key == srd.SRD_CONF_SAMPLERATE:
523 self.samplerate = value
524 # 0 is 2 UI, space larger than 1.5x 0 is definitely wrong.
525 self.maxbit = self.us2samples(3 * UI_US)
526 # Duration threshold between half 1 and 0.
527 self.threshold = self.us2samples(THRESHOLD_US)
530 self.out_ann = self.register(srd.OUTPUT_ANN)
531 self.out_binary = self.register(srd.OUTPUT_BINARY)
532 self.out_bitrate = self.register(
534 meta=(int, 'Bitrate', 'Bitrate during the packet')
537 def us2samples(self, us):
538 return int(us * self.samplerate / 1000000)
540 def decode_packet(self):
545 if len(self.edges) < 50:
546 return # Not a real PD packet
549 tstamp = float(self.startsample) / self.samplerate
550 self.text += '#%-4d (%8.6fms): ' % (self.packet_seq, tstamp*1000)
552 self.idx = self.scan_eop()
554 # Full text trace of the issue.
555 self.putx(0, self.idx, [12, [self.text, '...']])
556 return # No real packet: ABORT.
559 self.head = self.get_short()
560 self.putx(self.idx-20, self.idx, [3, ['H:%04x' % (self.head), 'HD']])
563 # Decode data payload
564 for i in range(self.head_count()):
565 self.data.append(self.get_word())
566 self.putx(self.idx-40, self.idx,
567 [4, ['[%d]%08x' % (i, self.data[i]), 'D%d' % (i)]])
568 self.putpayload(self.idx-40, self.idx, i)
571 self.crc = self.get_word()
572 ccrc = self.compute_crc32()
574 self.putwarn('Bad CRC %08x != %08x' % (self.crc, ccrc), 'CRC!')
575 self.putx(self.idx-40, self.idx, [5, ['CRC:%08x' % (self.crc), 'CRC']])
578 if len(self.bits) >= self.idx + 5 and self.get_sym(self.idx) == EOP:
579 self.putx(self.idx, self.idx + 5, [6, ['EOP', 'E']])
582 self.putwarn('No EOP', 'EOP!')
584 if self.options['fulltext'] == 'yes':
585 self.putx(0, self.idx, [12, [self.text, '...']])
587 # Meta data for bitrate
588 ss, es = self.edges[0], self.edges[-1]
589 bitrate = self.samplerate*len(self.bits) / float(es - ss)
590 self.put(es, ss, self.out_bitrate, int(bitrate))
591 # Raw binary data (BMC decoded)
592 self.put(es, ss, self.out_binary, [0, bytes(self.bits)])
595 if not self.samplerate:
596 raise SamplerateError('Cannot decode without samplerate.')
598 pins = self.wait([{0: 'e'}, {1: 'e'}, {'skip': int(self.samplerate/1e3)}])
600 # First sample of the packet, just record the start date.
601 if not self.startsample:
602 self.startsample = self.samplenum
603 self.previous = self.samplenum
606 diff = self.samplenum - self.previous
608 # Large idle: use it as the end of packet.
609 if diff > self.maxbit:
610 # The last edge of the packet.
611 self.edges.append(self.previous)
614 # Reset for next packet.
615 self.startsample = self.samplenum
619 self.half_one = False
621 else: # Add the bit to the packet.
622 is_zero = diff > self.threshold
623 if is_zero and not self.half_one:
625 self.edges.append(self.previous)
626 elif not is_zero and self.half_one:
628 self.edges.append(self.start_one)
629 self.half_one = False
630 elif not is_zero and not self.half_one:
632 self.start_one = self.previous
633 else: # Invalid BMC sequence
634 self.bad.append((self.start_one, self.previous))
635 # TODO: Try to recover.
637 self.edges.append(self.previous)
638 self.half_one = False
639 self.previous = self.samplenum