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.'
207 {'id': 'cc1', 'name': 'CC1', 'desc': 'Configuration Channel 1'},
209 optional_channels = (
210 {'id': 'cc2', 'name': 'CC2', 'desc': 'Configuration Channel 2'},
213 {'id': 'fulltext', 'desc': 'Full text decoding of packets',
214 'default': 'no', 'values': ('yes', 'no')},
217 ('type', 'Packet Type'),
218 ('preamble', 'Preamble'),
219 ('sop', 'Start of Packet'),
220 ('header', 'Header'),
223 ('eop', 'End Of Packet'),
224 ('sym', '4b5b symbols'),
225 ('warnings', 'Warnings'),
226 ('src', 'Source Message'),
227 ('snk', 'Sink Message'),
228 ('payload', 'Payload'),
229 ('text', 'Plain text'),
232 ('4b5b', 'Symbols', (7,)),
233 ('phase', 'Parts', (1, 2, 3, 4, 5, 6)),
234 ('payload', 'Payload', (11,)),
235 ('type', 'Type', (0, 9, 10)),
236 ('warnings', 'Warnings', (8,)),
237 ('text', 'Full text', (12,)),
240 ('raw-data', 'RAW binary data'),
245 def get_request(self, rdo):
246 pos = (rdo >> 28) & 7
248 op_ma = ((rdo >> 10) & 0x3ff) * 0.01
249 max_ma = (rdo & 0x3ff) * 0.01
251 mark = self.cap_mark[pos]
253 op_v = ((rdo >> 9) & 0x7ff) * 0.02
254 op_a = (rdo & 0x3f) * 0.05
255 t_settings = '%gV %gA' % (op_v, op_a)
257 op_w = ((rdo >> 10) & 0x3ff) * 0.25
258 mp_w = (rdo & 0x3ff) * 0.25
259 t_settings = '%gW (operating)' % op_w
261 op_a = ((rdo >> 10) & 0x3ff) * 0.01
262 max_a = (rdo & 0x3ff) * 0.01
263 t_settings = '%gA (operating) / %gA (max)' % (op_a, max_a)
266 for f in sorted(RDO_FLAGS.keys(), reverse = True):
268 t_flags += ' [' + RDO_FLAGS[f] + ']'
270 if pos in self.stored_pdos.keys():
271 t_pdo = '#%d: %s' % (pos, self.stored_pdos[pos])
275 return '(PDO %s) %s%s' % (t_pdo, t_settings, t_flags)
277 def get_source_sink_cap(self, pdo, idx, source):
279 self.cap_mark[idx] = t1
286 (1 << 29): 'dual_role_power',
287 (1 << 28): 'suspend',
288 (1 << 27): 'unconstrained',
289 (1 << 26): 'comm_cap',
290 (1 << 25): 'dual_role_data',
291 (1 << 24): 'unchunked',
295 (1 << 29): 'dual_role_power',
296 (1 << 28): 'high_capability',
297 (1 << 27): 'unconstrained',
298 (1 << 26): 'comm_cap',
299 (1 << 25): 'dual_role_data',
300 (0b01 << 23): 'fr_swap default power',
301 (0b10 << 23): 'fr_swap 1.5 A',
302 (0b11 << 23): 'fr_swap 3.0 A',
304 mv = ((pdo >> 10) & 0x3ff) * 0.05
305 ma = ((pdo >> 0) & 0x3ff) * 0.01
306 p = '%gV %gA (%gW)' % (mv, ma, mv*ma)
307 self.stored_pdos[idx] = '%s %gV' % (t_name, mv)
310 flags = {} # No flags defined for Battery PDO in PD 3.0 spec
311 minv = ((pdo >> 10) & 0x3ff) * 0.05
312 maxv = ((pdo >> 20) & 0x3ff) * 0.05
313 mw = ((pdo >> 0) & 0x3ff) * 0.25
314 p = '%g/%gV %gW' % (minv, maxv, mw)
315 self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv)
318 flags = {} # No flags defined for Variable PDO in PD 3.0 spec
319 minv = ((pdo >> 10) & 0x3ff) * 0.05
320 maxv = ((pdo >> 20) & 0x3ff) * 0.05
321 ma = ((pdo >> 0) & 0x3ff) * 0.01
322 p = '%g/%gV %gA' % (minv, maxv, ma)
323 self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv)
327 t_name = 'Programmable|PPS'
329 (1 << 29): 'power_limited',
331 minv = ((pdo >> 8) & 0xff) * 0.1
332 maxv = ((pdo >> 17) & 0xff) * 0.1
333 ma = ((pdo >> 0) & 0xff) * 0.05
334 p = '%g/%gV %gA' % (minv, maxv, ma)
335 if (pdo >> 27) & 0x1:
337 self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv)
339 t_name = 'Reserved APDO: '+bin(t2)
340 p = '[raw: %s]' % (bin(pdo))
341 self.stored_pdos[idx] = '%s %s' % (t_name, p)
343 for f in sorted(flags.keys(), reverse = True):
345 t_flags += ' [' + flags[f] + ']'
346 return '[%s] %s%s' % (t_name, p, t_flags)
348 def get_vdm(self, idx, data):
349 if idx == 0: # VDM header
351 struct = data & (1 << 15)
353 if struct: # Structured VDM
355 src = data & (1 << 5)
356 ack = (data >> 6) & 3
357 pos = (data >> 8) & 7
358 ver = (data >> 13) & 3
359 txt = VDM_ACK[ack] + ' '
360 txt += VDM_CMDS[cmd] if cmd in VDM_CMDS else 'cmd?'
361 txt += ' pos %d' % (pos) if pos else ' '
362 else: # Unstructured VDM
363 txt = 'unstruct [%04x]' % (data & 0x7fff)
364 txt += ' SVID:%04x' % (vid)
366 txt = 'VDO:%08x' % (data)
369 def get_bist(self, idx, data):
371 counter = data & 0xffff
372 mode_name = BIST_MODES[mode] if mode in BIST_MODES else 'INVALID'
374 mode_name = 'Counter[= %d]' % (counter)
375 # TODO: Check all 0 bits are 0 / emit warnings.
376 return 'mode %s' % (mode_name) if idx == 0 else 'invalid BRO'
378 def putpayload(self, s0, s1, idx):
380 txt = '['+str(idx+1)+'] '
382 txt += self.get_request(self.data[idx])
383 elif t == 1 or t == 4:
384 txt += self.get_source_sink_cap(self.data[idx], idx+1, t==1)
386 txt += self.get_vdm(idx, self.data[idx])
388 txt += self.get_bist(idx, self.data[idx])
389 self.putx(s0, s1, [11, [txt, txt]])
390 self.text += ' - ' + txt
393 ann_type = 9 if self.head_power_role() else 10
394 role = 'SRC' if self.head_power_role() else 'SNK'
395 if self.head_data_role() != self.head_power_role():
396 role += '/DFP' if self.head_data_role() else '/UFP'
398 if self.head_count() == 0:
399 shortm = CTRL_TYPES[t]
401 shortm = DATA_TYPES[t] if t in DATA_TYPES else 'DAT???'
403 longm = '(r{:d}) {:s}[{:d}]: {:s}'.format(self.head_rev(), role, self.head_id(), shortm)
404 self.putx(0, -1, [ann_type, [longm, shortm]])
408 return (self.head >> 9) & 7
410 def head_power_role(self):
411 return (self.head >> 8) & 1
413 def head_data_role(self):
414 return (self.head >> 5) & 1
417 return ((self.head >> 6) & 3) + 1
420 return self.head & 0xF
422 def head_count(self):
423 return (self.head >> 12) & 7
425 def putx(self, s0, s1, data):
426 self.put(self.edges[s0], self.edges[s1], self.out_ann, data)
428 def putwarn(self, longm, shortm):
429 self.putx(0, -1, [8, [longm, shortm]])
431 def compute_crc32(self):
432 bdata = struct.pack('<H'+'I'*len(self.data), self.head & 0xffff,
433 *tuple([d & 0xffffffff for d in self.data]))
434 return zlib.crc32(bdata)
436 def rec_sym(self, i, sym):
437 self.putx(i, i+5, [7, SYM_NAME[sym]])
439 def get_sym(self, i, rec=True):
440 v = (self.bits[i] | (self.bits[i+1] << 1) | (self.bits[i+2] << 2) |
441 (self.bits[i+3] << 3) | (self.bits[i+4] << 4))
449 # Check it's not a truncated packet.
450 if len(self.bits) - i <= 20:
451 self.putwarn('Truncated', '!')
453 k = [self.get_sym(i), self.get_sym(i+5),
454 self.get_sym(i+10), self.get_sym(i+15)]
455 # TODO: Check bad symbols.
456 val = k[0] | (k[1] << 4) | (k[2] << 8) | (k[3] << 12)
461 lo = self.get_short()
462 hi = self.get_short()
463 return lo | (hi << 16)
465 def find_corrupted_sop(self, k):
466 # Start of packet are valid even if they have only 3 correct symbols
468 for seq in SOP_SEQUENCES:
469 if [k[i] == seq[i] for i in range(len(k))].count(True) >= 3:
470 return START_OF_PACKETS[seq]
474 for i in range(len(self.bits) - 19):
475 k = (self.get_sym(i, rec=False), self.get_sym(i+5, rec=False),
476 self.get_sym(i+10, rec=False), self.get_sym(i+15, rec=False))
477 sym = START_OF_PACKETS.get(k, None)
479 sym = self.find_corrupted_sop(k)
480 # We have an interesting symbol sequence.
482 # Annotate the preamble.
483 self.putx(0, i, [1, ['Preamble', '...']])
484 # Annotate each symbol.
485 self.rec_sym(i, k[0])
486 self.rec_sym(i+5, k[1])
487 self.rec_sym(i+10, k[2])
488 self.rec_sym(i+15, k[3])
489 if sym == 'Hard Reset':
491 return -1 # Hard reset
492 elif sym == 'Cable Reset':
494 return -1 # Cable reset
496 self.putx(i, i+20, [2, [sym, 'S']])
498 self.putx(0, len(self.bits), [1, ['Junk???', 'XXX']])
499 self.text += 'Junk???'
500 self.putwarn('No start of packet found', 'XXX')
501 return -1 # No Start Of Packet
507 self.samplerate = None
511 self.startsample = None
515 self.half_one = False
517 self.stored_pdos = {}
518 self.cap_mark = [0, 0, 0, 0, 0, 0, 0, 0]
520 def metadata(self, key, value):
521 if key == srd.SRD_CONF_SAMPLERATE:
522 self.samplerate = value
523 # 0 is 2 UI, space larger than 1.5x 0 is definitely wrong.
524 self.maxbit = self.us2samples(3 * UI_US)
525 # Duration threshold between half 1 and 0.
526 self.threshold = self.us2samples(THRESHOLD_US)
529 self.out_ann = self.register(srd.OUTPUT_ANN)
530 self.out_binary = self.register(srd.OUTPUT_BINARY)
531 self.out_bitrate = self.register(
533 meta=(int, 'Bitrate', 'Bitrate during the packet')
536 def us2samples(self, us):
537 return int(us * self.samplerate / 1000000)
539 def decode_packet(self):
544 if len(self.edges) < 50:
545 return # Not a real PD packet
548 tstamp = float(self.startsample) / self.samplerate
549 self.text += '#%-4d (%8.6fms): ' % (self.packet_seq, tstamp*1000)
551 self.idx = self.scan_eop()
553 # Full text trace of the issue.
554 self.putx(0, self.idx, [12, [self.text, '...']])
555 return # No real packet: ABORT.
558 self.head = self.get_short()
559 self.putx(self.idx-20, self.idx, [3, ['H:%04x' % (self.head), 'HD']])
562 # Decode data payload
563 for i in range(self.head_count()):
564 self.data.append(self.get_word())
565 self.putx(self.idx-40, self.idx,
566 [4, ['[%d]%08x' % (i, self.data[i]), 'D%d' % (i)]])
567 self.putpayload(self.idx-40, self.idx, i)
570 self.crc = self.get_word()
571 ccrc = self.compute_crc32()
573 self.putwarn('Bad CRC %08x != %08x' % (self.crc, ccrc), 'CRC!')
574 self.putx(self.idx-40, self.idx, [5, ['CRC:%08x' % (self.crc), 'CRC']])
577 if len(self.bits) >= self.idx + 5 and self.get_sym(self.idx) == EOP:
578 self.putx(self.idx, self.idx + 5, [6, ['EOP', 'E']])
581 self.putwarn('No EOP', 'EOP!')
583 if self.options['fulltext'] == 'yes':
584 self.putx(0, self.idx, [12, [self.text, '...']])
586 # Meta data for bitrate
587 ss, es = self.edges[0], self.edges[-1]
588 bitrate = self.samplerate*len(self.bits) / float(es - ss)
589 self.put(es, ss, self.out_bitrate, int(bitrate))
590 # Raw binary data (BMC decoded)
591 self.put(es, ss, self.out_binary, [0, bytes(self.bits)])
594 if not self.samplerate:
595 raise SamplerateError('Cannot decode without samplerate.')
597 pins = self.wait([{0: 'e'}, {1: 'e'}, {'skip': int(self.samplerate/1e3)}])
599 # First sample of the packet, just record the start date.
600 if not self.startsample:
601 self.startsample = self.samplenum
602 self.previous = self.samplenum
605 diff = self.samplenum - self.previous
607 # Large idle: use it as the end of packet.
608 if diff > self.maxbit:
609 # The last edge of the packet.
610 self.edges.append(self.previous)
613 # Reset for next packet.
614 self.startsample = self.samplenum
618 self.half_one = False
620 else: # Add the bit to the packet.
621 is_zero = diff > self.threshold
622 if is_zero and not self.half_one:
624 self.edges.append(self.previous)
625 elif not is_zero and self.half_one:
627 self.edges.append(self.start_one)
628 self.half_one = False
629 elif not is_zero and not self.half_one:
631 self.start_one = self.previous
632 else: # Invalid BMC sequence
633 self.bad.append((self.start_one, self.previous))
634 # TODO: Try to recover.
636 self.edges.append(self.previous)
637 self.half_one = False
638 self.previous = self.samplenum