]> sigrok.org Git - libsigrokdecode.git/blame - decoders/usb_power_delivery/pd.py
decoders: Add/update tags for each PD.
[libsigrokdecode.git] / decoders / usb_power_delivery / pd.py
CommitLineData
ced6589f
VP
1##
2## This file is part of the libsigrokdecode project.
3##
fddb3114 4## Copyright (C) 2015 Google, Inc
9d54b24e 5## Copyright (C) 2018 davidanger <davidanger@163.com>
4083a379 6## Copyright (C) 2018 Peter Hazenberg <sigrok@haas-en-berg.nl>
ced6589f
VP
7##
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.
12##
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.
17##
fddb3114 18## You should have received a copy of the GNU General Public License
4539e9ca 19## along with this program; if not, see <http://www.gnu.org/licenses/>.
fddb3114 20##
ced6589f
VP
21
22import sigrokdecode as srd
23import struct
24import zlib # for crc32
25
79065c6f 26# BMC encoding with a 600kHz datarate
ced6589f
VP
27UI_US = 1000000/600000.0
28
29# Threshold to discriminate half-1 from 0 in Binary Mark Conding
30THRESHOLD_US = (UI_US + 2 * UI_US) / 2
31
32# Control Message type
33CTRL_TYPES = {
89b9aaf7
VP
34 0: 'reserved',
35 1: 'GOOD CRC',
36 2: 'GOTO MIN',
37 3: 'ACCEPT',
38 4: 'REJECT',
39 5: 'PING',
40 6: 'PS RDY',
41 7: 'GET SOURCE CAP',
42 8: 'GET SINK CAP',
43 9: 'DR SWAP',
44 10: 'PR SWAP',
45 11: 'VCONN SWAP',
46 12: 'WAIT',
47 13: 'SOFT RESET',
48 14: 'reserved',
9d54b24e 49 15: 'reserved',
50 16: 'Not Supported',
51 17: 'Get_Source_Cap_Extended',
52 18: 'Get_Status',
53 19: 'FR_Swap',
54 20: 'Get_PPS_Status',
55 21: 'Get_Country_Codes',
ced6589f
VP
56}
57
58# Data message type
59DATA_TYPES = {
89b9aaf7
VP
60 1: 'SOURCE CAP',
61 2: 'REQUEST',
62 3: 'BIST',
63 4: 'SINK CAP',
9d54b24e 64 5: 'Battery_Status',
65 6: 'Alert',
66 7: 'Get_Country_Info',
89b9aaf7 67 15: 'VDM'
ced6589f
VP
68}
69
70# 4b5b encoding of the symbols
71DEC4B5B = [
72 0x10, # Error 00000
73 0x10, # Error 00001
74 0x10, # Error 00010
75 0x10, # Error 00011
76 0x10, # Error 00100
77 0x10, # Error 00101
78 0x13, # Sync-3 00110
79 0x14, # RST-1 00111
80 0x10, # Error 01000
81 0x01, # 1 = 0001 01001
82 0x04, # 4 = 0100 01010
83 0x05, # 5 = 0101 01011
84 0x10, # Error 01100
85 0x16, # EOP 01101
86 0x06, # 6 = 0110 01110
87 0x07, # 7 = 0111 01111
88 0x10, # Error 10000
89 0x12, # Sync-2 10001
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
96 0x11, # Sync-1 11000
97 0x15, # RST-2 11001
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
103 0x10, # Error 11111
104]
105SYM_ERR = 0x10
106SYNC1 = 0x11
107SYNC2 = 0x12
108SYNC3 = 0x13
109RST1 = 0x14
110RST2 = 0x15
111EOP = 0x16
112SYNC_CODES = [SYNC1, SYNC2, SYNC3]
113HRST_CODES = [RST1, RST1, RST1, RST2]
114
f30fdbb6
GS
115SOP_SEQUENCES = [
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),
123]
ced6589f 124START_OF_PACKETS = {
f30fdbb6
GS
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',
ced6589f
VP
132}
133
134SYM_NAME = [
135 ['0x0', '0'],
136 ['0x1', '1'],
137 ['0x2', '2'],
138 ['0x3', '3'],
139 ['0x4', '4'],
140 ['0x5', '5'],
141 ['0x6', '6'],
142 ['0x7', '7'],
143 ['0x8', '8'],
144 ['0x9', '9'],
145 ['0xA', 'A'],
146 ['0xB', 'B'],
147 ['0xC', 'C'],
148 ['0xD', 'D'],
149 ['0xE', 'E'],
150 ['0xF', 'F'],
151 ['ERROR', 'X'],
152 ['SYNC-1', 'S1'],
153 ['SYNC-2', 'S2'],
154 ['SYNC-3', 'S3'],
155 ['RST-1', 'R1'],
156 ['RST-2', 'R2'],
157 ['EOP', '#'],
158]
159
160RDO_FLAGS = {
9d54b24e 161 (1 << 23): 'unchunked',
89b9aaf7
VP
162 (1 << 24): 'no_suspend',
163 (1 << 25): 'comm_cap',
164 (1 << 26): 'cap_mismatch',
165 (1 << 27): 'give_back'
ced6589f 166}
4083a379 167
ced6589f 168BIST_MODES = {
89b9aaf7
VP
169 0: 'Receiver',
170 1: 'Transmit',
171 2: 'Counters',
172 3: 'Carrier 0',
173 4: 'Carrier 1',
174 5: 'Carrier 2',
175 6: 'Carrier 3',
176 7: 'Eye',
ced6589f
VP
177}
178
179VDM_CMDS = {
89b9aaf7
VP
180 1: 'Disc Ident',
181 2: 'Disc SVID',
182 3: 'Disc Mode',
183 4: 'Enter Mode',
184 5: 'Exit Mode',
185 6: 'Attention',
ced6589f
VP
186 # 16..31: SVID Specific Commands
187 # DisplayPort Commands
89b9aaf7
VP
188 16: 'DP Status',
189 17: 'DP Configure',
ced6589f 190}
89b9aaf7 191VDM_ACK = ['REQ', 'ACK', 'NAK', 'BSY']
ced6589f 192
4083a379 193
669f30f4
UH
194class SamplerateError(Exception):
195 pass
196
ced6589f 197class Decoder(srd.Decoder):
bc6f82bb 198 api_version = 3
ced6589f
VP
199 id = 'usb_power_delivery'
200 name = 'USB PD'
201 longname = 'USB Power Delivery'
202 desc = 'USB Power Delivery protocol.'
203 license = 'gplv2+'
204 inputs = ['logic']
205 outputs = ['usb_pd']
d6d8a8a4 206 tags = ['PC']
ced6589f 207 channels = (
57567cab 208 {'id': 'cc1', 'name': 'CC1', 'desc': 'Configuration Channel 1'},
a886ba3b
P
209 )
210 optional_channels = (
57567cab 211 {'id': 'cc2', 'name': 'CC2', 'desc': 'Configuration Channel 2'},
ced6589f
VP
212 )
213 options = (
43047b89 214 {'id': 'fulltext', 'desc': 'Full text decoding of packets',
ced6589f
VP
215 'default': 'no', 'values': ('yes', 'no')},
216 )
217 annotations = (
218 ('type', 'Packet Type'),
43047b89
UH
219 ('preamble', 'Preamble'),
220 ('sop', 'Start of Packet'),
221 ('header', 'Header'),
222 ('data', 'Data'),
223 ('crc', 'Checksum'),
224 ('eop', 'End Of Packet'),
225 ('sym', '4b5b symbols'),
ced6589f
VP
226 ('warnings', 'Warnings'),
227 ('src', 'Source Message'),
228 ('snk', 'Sink Message'),
229 ('payload', 'Payload'),
230 ('text', 'Plain text'),
231 )
232 annotation_rows = (
43047b89
UH
233 ('4b5b', 'Symbols', (7,)),
234 ('phase', 'Parts', (1, 2, 3, 4, 5, 6)),
235 ('payload', 'Payload', (11,)),
236 ('type', 'Type', (0, 9, 10)),
237 ('warnings', 'Warnings', (8,)),
238 ('text', 'Full text', (12,)),
ced6589f
VP
239 )
240 binary = (
241 ('raw-data', 'RAW binary data'),
242 )
243
a886ba3b
P
244 stored_pdos = {}
245
ced6589f
VP
246 def get_request(self, rdo):
247 pos = (rdo >> 28) & 7
9d54b24e 248
a886ba3b
P
249 op_ma = ((rdo >> 10) & 0x3ff) * 0.01
250 max_ma = (rdo & 0x3ff) * 0.01
9d54b24e 251
252 mark = self.cap_mark[pos]
253 if mark == 3:
254 op_v = ((rdo >> 9) & 0x7ff) * 0.02
255 op_a = (rdo & 0x3f) * 0.05
256 t_settings = '%gV %gA' % (op_v, op_a)
257 elif mark == 2:
258 op_w = ((rdo >> 10) & 0x3ff) * 0.25
259 mp_w = (rdo & 0x3ff) * 0.25
d0d63dea 260 t_settings = '%gW (operating)' % op_w
9d54b24e 261 else:
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)
265
266 t_flags = ''
57ba804a 267 for f in sorted(RDO_FLAGS.keys(), reverse = True):
ced6589f 268 if rdo & f:
9d54b24e 269 t_flags += ' [' + RDO_FLAGS[f] + ']'
270
a886ba3b 271 if pos in self.stored_pdos.keys():
9d54b24e 272 t_pdo = '#%d: %s' % (pos, self.stored_pdos[pos])
a886ba3b 273 else:
2eeca7bd 274 t_pdo = '#%d' % (pos)
ced6589f 275
9d54b24e 276 return '(PDO %s) %s%s' % (t_pdo, t_settings, t_flags)
277
278 def get_source_sink_cap(self, pdo, idx, source):
4083a379 279 t1 = (pdo >> 30) & 3
9d54b24e 280 self.cap_mark[idx] = t1
281
282 flags = {}
4083a379
P
283 if t1 == 0:
284 t_name = 'Fixed'
9d54b24e 285 if source:
286 flags = {
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',
293 }
43047b89 294 else: # Sink
9d54b24e 295 flags = {
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',
304 }
4083a379 305 mv = ((pdo >> 10) & 0x3ff) * 0.05
9d54b24e 306 ma = ((pdo >> 0) & 0x3ff) * 0.01
4083a379 307 p = '%gV %gA (%gW)' % (mv, ma, mv*ma)
a886ba3b 308 self.stored_pdos[idx] = '%s %gV' % (t_name, mv)
4083a379
P
309 elif t1 == 1:
310 t_name = 'Battery'
9d54b24e 311 flags = {} # No flags defined for Battery PDO in PD 3.0 spec
4083a379
P
312 minv = ((pdo >> 10) & 0x3ff) * 0.05
313 maxv = ((pdo >> 20) & 0x3ff) * 0.05
9d54b24e 314 mw = ((pdo >> 0) & 0x3ff) * 0.25
4083a379 315 p = '%g/%gV %gW' % (minv, maxv, mw)
a886ba3b 316 self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv)
4083a379
P
317 elif t1 == 2:
318 t_name = 'Variable'
9d54b24e 319 flags = {} # No flags defined for Variable PDO in PD 3.0 spec
4083a379
P
320 minv = ((pdo >> 10) & 0x3ff) * 0.05
321 maxv = ((pdo >> 20) & 0x3ff) * 0.05
9d54b24e 322 ma = ((pdo >> 0) & 0x3ff) * 0.01
4083a379 323 p = '%g/%gV %gA' % (minv, maxv, ma)
a886ba3b 324 self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv)
4083a379
P
325 elif t1 == 3:
326 t2 = (pdo >> 28) & 3
327 if t2 == 0:
603a3c77 328 t_name = 'Programmable|PPS'
9d54b24e 329 flags = {
330 (1 << 29): 'power_limited',
331 }
603a3c77
P
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:
337 p += ' [limited]'
338 self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv)
4083a379
P
339 else:
340 t_name = 'Reserved APDO: '+bin(t2)
603a3c77
P
341 p = '[raw: %s]' % (bin(pdo))
342 self.stored_pdos[idx] = '%s %s' % (t_name, p)
9d54b24e 343 t_flags = ''
344 for f in sorted(flags.keys(), reverse = True):
ced6589f 345 if pdo & f:
9d54b24e 346 t_flags += ' [' + flags[f] + ']'
347 return '[%s] %s%s' % (t_name, p, t_flags)
ced6589f
VP
348
349 def get_vdm(self, idx, data):
43047b89 350 if idx == 0: # VDM header
79065c6f
UH
351 vid = data >> 16
352 struct = data & (1 << 15)
89b9aaf7 353 txt = 'VDM'
43047b89 354 if struct: # Structured VDM
79065c6f
UH
355 cmd = data & 0x1f
356 src = data & (1 << 5)
357 ack = (data >> 6) & 3
358 pos = (data >> 8) & 7
359 ver = (data >> 13) & 3
89b9aaf7
VP
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 ' '
43047b89 363 else: # Unstructured VDM
89b9aaf7
VP
364 txt = 'unstruct [%04x]' % (data & 0x7fff)
365 txt += ' SVID:%04x' % (vid)
43047b89 366 else: # VDM payload
89b9aaf7 367 txt = 'VDO:%08x' % (data)
ced6589f
VP
368 return txt
369
370 def get_bist(self, idx, data):
371 mode = data >> 28
372 counter = data & 0xffff
89b9aaf7 373 mode_name = BIST_MODES[mode] if mode in BIST_MODES else 'INVALID'
ced6589f 374 if mode == 2:
89b9aaf7 375 mode_name = 'Counter[= %d]' % (counter)
43047b89 376 # TODO: Check all 0 bits are 0 / emit warnings.
89b9aaf7 377 return 'mode %s' % (mode_name) if idx == 0 else 'invalid BRO'
ced6589f
VP
378
379 def putpayload(self, s0, s1, idx):
380 t = self.head_type()
4083a379 381 txt = '['+str(idx+1)+'] '
ced6589f 382 if t == 2:
4083a379
P
383 txt += self.get_request(self.data[idx])
384 elif t == 1 or t == 4:
9d54b24e 385 txt += self.get_source_sink_cap(self.data[idx], idx+1, t==1)
ced6589f 386 elif t == 15:
4083a379 387 txt += self.get_vdm(idx, self.data[idx])
ced6589f 388 elif t == 3:
4083a379 389 txt += self.get_bist(idx, self.data[idx])
ced6589f 390 self.putx(s0, s1, [11, [txt, txt]])
89b9aaf7 391 self.text += ' - ' + txt
ced6589f
VP
392
393 def puthead(self):
394 ann_type = 9 if self.head_power_role() else 10
89b9aaf7 395 role = 'SRC' if self.head_power_role() else 'SNK'
ced6589f 396 if self.head_data_role() != self.head_power_role():
89b9aaf7 397 role += '/DFP' if self.head_data_role() else '/UFP'
ced6589f
VP
398 t = self.head_type()
399 if self.head_count() == 0:
400 shortm = CTRL_TYPES[t]
401 else:
89b9aaf7 402 shortm = DATA_TYPES[t] if t in DATA_TYPES else 'DAT???'
ced6589f 403
9d54b24e 404 longm = '(r{:d}) {:s}[{:d}]: {:s}'.format(self.head_rev(), role, self.head_id(), shortm)
ced6589f
VP
405 self.putx(0, -1, [ann_type, [longm, shortm]])
406 self.text += longm
407
408 def head_id(self):
409 return (self.head >> 9) & 7
410
411 def head_power_role(self):
412 return (self.head >> 8) & 1
413
414 def head_data_role(self):
415 return (self.head >> 5) & 1
416
417 def head_rev(self):
418 return ((self.head >> 6) & 3) + 1
419
420 def head_type(self):
421 return self.head & 0xF
422
423 def head_count(self):
424 return (self.head >> 12) & 7
425
426 def putx(self, s0, s1, data):
427 self.put(self.edges[s0], self.edges[s1], self.out_ann, data)
428
429 def putwarn(self, longm, shortm):
430 self.putx(0, -1, [8, [longm, shortm]])
431
432 def compute_crc32(self):
89b9aaf7 433 bdata = struct.pack('<H'+'I'*len(self.data), self.head & 0xffff,
ced6589f
VP
434 *tuple([d & 0xffffffff for d in self.data]))
435 return zlib.crc32(bdata)
436
437 def rec_sym(self, i, sym):
438 self.putx(i, i+5, [7, SYM_NAME[sym]])
439
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))
443 sym = DEC4B5B[v]
444 if rec:
445 self.rec_sym(i, sym)
446 return sym
447
448 def get_short(self):
449 i = self.idx
43047b89 450 # Check it's not a truncated packet.
ced6589f 451 if len(self.bits) - i <= 20:
89b9aaf7 452 self.putwarn('Truncated', '!')
ced6589f
VP
453 return 0x0BAD
454 k = [self.get_sym(i), self.get_sym(i+5),
455 self.get_sym(i+10), self.get_sym(i+15)]
43047b89 456 # TODO: Check bad symbols.
ced6589f
VP
457 val = k[0] | (k[1] << 4) | (k[2] << 8) | (k[3] << 12)
458 self.idx += 20
459 return val
460
461 def get_word(self):
462 lo = self.get_short()
463 hi = self.get_short()
464 return lo | (hi << 16)
465
466 def find_corrupted_sop(self, k):
467 # Start of packet are valid even if they have only 3 correct symbols
468 # out of 4.
f30fdbb6 469 for seq in SOP_SEQUENCES:
ced6589f
VP
470 if [k[i] == seq[i] for i in range(len(k))].count(True) >= 3:
471 return START_OF_PACKETS[seq]
472 return None
473
474 def scan_eop(self):
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))
279331bd 478 sym = START_OF_PACKETS.get(k, None)
ced6589f
VP
479 if not sym:
480 sym = self.find_corrupted_sop(k)
43047b89 481 # We have an interesting symbol sequence.
ced6589f 482 if sym:
43047b89 483 # Annotate the preamble.
ced6589f 484 self.putx(0, i, [1, ['Preamble', '...']])
43047b89 485 # Annotate each symbol.
ced6589f
VP
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':
89b9aaf7 491 self.text += 'HRST'
43047b89 492 return -1 # Hard reset
ced6589f 493 elif sym == 'Cable Reset':
89b9aaf7 494 self.text += 'CRST'
43047b89 495 return -1 # Cable reset
ced6589f
VP
496 else:
497 self.putx(i, i+20, [2, [sym, 'S']])
498 return i+20
499 self.putx(0, len(self.bits), [1, ['Junk???', 'XXX']])
89b9aaf7
VP
500 self.text += 'Junk???'
501 self.putwarn('No start of packet found', 'XXX')
43047b89 502 return -1 # No Start Of Packet
ced6589f 503
92b7b49f 504 def __init__(self):
10aeb8ea
GS
505 self.reset()
506
507 def reset(self):
ced6589f
VP
508 self.samplerate = None
509 self.idx = 0
510 self.packet_seq = 0
ced6589f 511 self.previous = 0
ced6589f
VP
512 self.startsample = None
513 self.bits = []
514 self.edges = []
515 self.bad = []
516 self.half_one = False
517 self.start_one = 0
a886ba3b 518 self.stored_pdos = {}
9d54b24e 519 self.cap_mark = [0, 0, 0, 0, 0, 0, 0, 0]
ced6589f
VP
520
521 def metadata(self, key, value):
522 if key == srd.SRD_CONF_SAMPLERATE:
523 self.samplerate = value
43047b89 524 # 0 is 2 UI, space larger than 1.5x 0 is definitely wrong.
ced6589f 525 self.maxbit = self.us2samples(3 * UI_US)
43047b89 526 # Duration threshold between half 1 and 0.
ced6589f
VP
527 self.threshold = self.us2samples(THRESHOLD_US)
528
529 def start(self):
ced6589f
VP
530 self.out_ann = self.register(srd.OUTPUT_ANN)
531 self.out_binary = self.register(srd.OUTPUT_BINARY)
532 self.out_bitrate = self.register(
533 srd.OUTPUT_META,
534 meta=(int, 'Bitrate', 'Bitrate during the packet')
535 )
536
537 def us2samples(self, us):
ced6589f
VP
538 return int(us * self.samplerate / 1000000)
539
540 def decode_packet(self):
541 self.data = []
542 self.idx = 0
89b9aaf7 543 self.text = ''
ced6589f
VP
544
545 if len(self.edges) < 50:
43047b89 546 return # Not a real PD packet
ced6589f
VP
547
548 self.packet_seq += 1
549 tstamp = float(self.startsample) / self.samplerate
89b9aaf7 550 self.text += '#%-4d (%8.6fms): ' % (self.packet_seq, tstamp*1000)
ced6589f
VP
551
552 self.idx = self.scan_eop()
553 if self.idx < 0:
43047b89 554 # Full text trace of the issue.
ced6589f 555 self.putx(0, self.idx, [12, [self.text, '...']])
43047b89 556 return # No real packet: ABORT.
ced6589f
VP
557
558 # Packet header
559 self.head = self.get_short()
560 self.putx(self.idx-20, self.idx, [3, ['H:%04x' % (self.head), 'HD']])
561 self.puthead()
562
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)
569
570 # CRC check
571 self.crc = self.get_word()
572 ccrc = self.compute_crc32()
573 if self.crc != ccrc:
89b9aaf7 574 self.putwarn('Bad CRC %08x != %08x' % (self.crc, ccrc), 'CRC!')
ced6589f
VP
575 self.putx(self.idx-40, self.idx, [5, ['CRC:%08x' % (self.crc), 'CRC']])
576
577 # End of Packet
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']])
580 self.idx += 5
581 else:
89b9aaf7 582 self.putwarn('No EOP', 'EOP!')
ced6589f
VP
583 # Full text trace
584 if self.options['fulltext'] == 'yes':
585 self.putx(0, self.idx, [12, [self.text, '...']])
586
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)
2824e811 592 self.put(es, ss, self.out_binary, [0, bytes(self.bits)])
ced6589f 593
bc6f82bb 594 def decode(self):
033e7d4d 595 if not self.samplerate:
669f30f4 596 raise SamplerateError('Cannot decode without samplerate.')
bc6f82bb 597 while True:
df167c0f 598 pins = self.wait([{0: 'e'}, {1: 'e'}, {'skip': int(self.samplerate/1e3)}])
ced6589f 599
43047b89 600 # First sample of the packet, just record the start date.
ced6589f
VP
601 if not self.startsample:
602 self.startsample = self.samplenum
603 self.previous = self.samplenum
604 continue
605
606 diff = self.samplenum - self.previous
607
43047b89 608 # Large idle: use it as the end of packet.
ced6589f 609 if diff > self.maxbit:
43047b89 610 # The last edge of the packet.
ced6589f 611 self.edges.append(self.previous)
43047b89 612 # Export the packet.
ced6589f 613 self.decode_packet()
43047b89 614 # Reset for next packet.
ced6589f
VP
615 self.startsample = self.samplenum
616 self.bits = []
617 self.edges = []
618 self.bad = []
619 self.half_one = False
620 self.start_one = 0
43047b89 621 else: # Add the bit to the packet.
ced6589f
VP
622 is_zero = diff > self.threshold
623 if is_zero and not self.half_one:
624 self.bits.append(0)
625 self.edges.append(self.previous)
626 elif not is_zero and self.half_one:
627 self.bits.append(1)
628 self.edges.append(self.start_one)
629 self.half_one = False
630 elif not is_zero and not self.half_one:
631 self.half_one = True
632 self.start_one = self.previous
43047b89 633 else: # Invalid BMC sequence
ced6589f 634 self.bad.append((self.start_one, self.previous))
43047b89 635 # TODO: Try to recover.
ced6589f
VP
636 self.bits.append(0)
637 self.edges.append(self.previous)
638 self.half_one = False
639 self.previous = self.samplenum