]> sigrok.org Git - libsigrokdecode.git/blame - decoders/usb_power_delivery/pd.py
usb_power_delivery: Whitespace/consistency cosmetics.
[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']
206 channels = (
57567cab 207 {'id': 'cc1', 'name': 'CC1', 'desc': 'Configuration Channel 1'},
a886ba3b
P
208 )
209 optional_channels = (
57567cab 210 {'id': 'cc2', 'name': 'CC2', 'desc': 'Configuration Channel 2'},
ced6589f
VP
211 )
212 options = (
43047b89 213 {'id': 'fulltext', 'desc': 'Full text decoding of packets',
ced6589f
VP
214 'default': 'no', 'values': ('yes', 'no')},
215 )
216 annotations = (
217 ('type', 'Packet Type'),
43047b89
UH
218 ('preamble', 'Preamble'),
219 ('sop', 'Start of Packet'),
220 ('header', 'Header'),
221 ('data', 'Data'),
222 ('crc', 'Checksum'),
223 ('eop', 'End Of Packet'),
224 ('sym', '4b5b symbols'),
ced6589f
VP
225 ('warnings', 'Warnings'),
226 ('src', 'Source Message'),
227 ('snk', 'Sink Message'),
228 ('payload', 'Payload'),
229 ('text', 'Plain text'),
230 )
231 annotation_rows = (
43047b89
UH
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,)),
ced6589f
VP
238 )
239 binary = (
240 ('raw-data', 'RAW binary data'),
241 )
242
a886ba3b
P
243 stored_pdos = {}
244
ced6589f
VP
245 def get_request(self, rdo):
246 pos = (rdo >> 28) & 7
9d54b24e 247
a886ba3b
P
248 op_ma = ((rdo >> 10) & 0x3ff) * 0.01
249 max_ma = (rdo & 0x3ff) * 0.01
9d54b24e 250
251 mark = self.cap_mark[pos]
252 if mark == 3:
253 op_v = ((rdo >> 9) & 0x7ff) * 0.02
254 op_a = (rdo & 0x3f) * 0.05
255 t_settings = '%gV %gA' % (op_v, op_a)
256 elif mark == 2:
257 op_w = ((rdo >> 10) & 0x3ff) * 0.25
258 mp_w = (rdo & 0x3ff) * 0.25
d0d63dea 259 t_settings = '%gW (operating)' % op_w
9d54b24e 260 else:
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)
264
265 t_flags = ''
57ba804a 266 for f in sorted(RDO_FLAGS.keys(), reverse = True):
ced6589f 267 if rdo & f:
9d54b24e 268 t_flags += ' [' + RDO_FLAGS[f] + ']'
269
a886ba3b 270 if pos in self.stored_pdos.keys():
9d54b24e 271 t_pdo = '#%d: %s' % (pos, self.stored_pdos[pos])
a886ba3b 272 else:
9d54b24e 273 t_pdo = '#d' % (pos)
ced6589f 274
9d54b24e 275 return '(PDO %s) %s%s' % (t_pdo, t_settings, t_flags)
276
277 def get_source_sink_cap(self, pdo, idx, source):
4083a379 278 t1 = (pdo >> 30) & 3
9d54b24e 279 self.cap_mark[idx] = t1
280
281 flags = {}
4083a379
P
282 if t1 == 0:
283 t_name = 'Fixed'
9d54b24e 284 if source:
285 flags = {
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',
292 }
43047b89 293 else: # Sink
9d54b24e 294 flags = {
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',
303 }
4083a379 304 mv = ((pdo >> 10) & 0x3ff) * 0.05
9d54b24e 305 ma = ((pdo >> 0) & 0x3ff) * 0.01
4083a379 306 p = '%gV %gA (%gW)' % (mv, ma, mv*ma)
a886ba3b 307 self.stored_pdos[idx] = '%s %gV' % (t_name, mv)
4083a379
P
308 elif t1 == 1:
309 t_name = 'Battery'
9d54b24e 310 flags = {} # No flags defined for Battery PDO in PD 3.0 spec
4083a379
P
311 minv = ((pdo >> 10) & 0x3ff) * 0.05
312 maxv = ((pdo >> 20) & 0x3ff) * 0.05
9d54b24e 313 mw = ((pdo >> 0) & 0x3ff) * 0.25
4083a379 314 p = '%g/%gV %gW' % (minv, maxv, mw)
a886ba3b 315 self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv)
4083a379
P
316 elif t1 == 2:
317 t_name = 'Variable'
9d54b24e 318 flags = {} # No flags defined for Variable PDO in PD 3.0 spec
4083a379
P
319 minv = ((pdo >> 10) & 0x3ff) * 0.05
320 maxv = ((pdo >> 20) & 0x3ff) * 0.05
9d54b24e 321 ma = ((pdo >> 0) & 0x3ff) * 0.01
4083a379 322 p = '%g/%gV %gA' % (minv, maxv, ma)
a886ba3b 323 self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv)
4083a379
P
324 elif t1 == 3:
325 t2 = (pdo >> 28) & 3
326 if t2 == 0:
603a3c77 327 t_name = 'Programmable|PPS'
9d54b24e 328 flags = {
329 (1 << 29): 'power_limited',
330 }
603a3c77
P
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:
336 p += ' [limited]'
337 self.stored_pdos[idx] = '%s %g/%gV' % (t_name, minv, maxv)
4083a379
P
338 else:
339 t_name = 'Reserved APDO: '+bin(t2)
603a3c77
P
340 p = '[raw: %s]' % (bin(pdo))
341 self.stored_pdos[idx] = '%s %s' % (t_name, p)
9d54b24e 342 t_flags = ''
343 for f in sorted(flags.keys(), reverse = True):
ced6589f 344 if pdo & f:
9d54b24e 345 t_flags += ' [' + flags[f] + ']'
346 return '[%s] %s%s' % (t_name, p, t_flags)
ced6589f
VP
347
348 def get_vdm(self, idx, data):
43047b89 349 if idx == 0: # VDM header
79065c6f
UH
350 vid = data >> 16
351 struct = data & (1 << 15)
89b9aaf7 352 txt = 'VDM'
43047b89 353 if struct: # Structured VDM
79065c6f
UH
354 cmd = data & 0x1f
355 src = data & (1 << 5)
356 ack = (data >> 6) & 3
357 pos = (data >> 8) & 7
358 ver = (data >> 13) & 3
89b9aaf7
VP
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 ' '
43047b89 362 else: # Unstructured VDM
89b9aaf7
VP
363 txt = 'unstruct [%04x]' % (data & 0x7fff)
364 txt += ' SVID:%04x' % (vid)
43047b89 365 else: # VDM payload
89b9aaf7 366 txt = 'VDO:%08x' % (data)
ced6589f
VP
367 return txt
368
369 def get_bist(self, idx, data):
370 mode = data >> 28
371 counter = data & 0xffff
89b9aaf7 372 mode_name = BIST_MODES[mode] if mode in BIST_MODES else 'INVALID'
ced6589f 373 if mode == 2:
89b9aaf7 374 mode_name = 'Counter[= %d]' % (counter)
43047b89 375 # TODO: Check all 0 bits are 0 / emit warnings.
89b9aaf7 376 return 'mode %s' % (mode_name) if idx == 0 else 'invalid BRO'
ced6589f
VP
377
378 def putpayload(self, s0, s1, idx):
379 t = self.head_type()
4083a379 380 txt = '['+str(idx+1)+'] '
ced6589f 381 if t == 2:
4083a379
P
382 txt += self.get_request(self.data[idx])
383 elif t == 1 or t == 4:
9d54b24e 384 txt += self.get_source_sink_cap(self.data[idx], idx+1, t==1)
ced6589f 385 elif t == 15:
4083a379 386 txt += self.get_vdm(idx, self.data[idx])
ced6589f 387 elif t == 3:
4083a379 388 txt += self.get_bist(idx, self.data[idx])
ced6589f 389 self.putx(s0, s1, [11, [txt, txt]])
89b9aaf7 390 self.text += ' - ' + txt
ced6589f
VP
391
392 def puthead(self):
393 ann_type = 9 if self.head_power_role() else 10
89b9aaf7 394 role = 'SRC' if self.head_power_role() else 'SNK'
ced6589f 395 if self.head_data_role() != self.head_power_role():
89b9aaf7 396 role += '/DFP' if self.head_data_role() else '/UFP'
ced6589f
VP
397 t = self.head_type()
398 if self.head_count() == 0:
399 shortm = CTRL_TYPES[t]
400 else:
89b9aaf7 401 shortm = DATA_TYPES[t] if t in DATA_TYPES else 'DAT???'
ced6589f 402
9d54b24e 403 longm = '(r{:d}) {:s}[{:d}]: {:s}'.format(self.head_rev(), role, self.head_id(), shortm)
ced6589f
VP
404 self.putx(0, -1, [ann_type, [longm, shortm]])
405 self.text += longm
406
407 def head_id(self):
408 return (self.head >> 9) & 7
409
410 def head_power_role(self):
411 return (self.head >> 8) & 1
412
413 def head_data_role(self):
414 return (self.head >> 5) & 1
415
416 def head_rev(self):
417 return ((self.head >> 6) & 3) + 1
418
419 def head_type(self):
420 return self.head & 0xF
421
422 def head_count(self):
423 return (self.head >> 12) & 7
424
425 def putx(self, s0, s1, data):
426 self.put(self.edges[s0], self.edges[s1], self.out_ann, data)
427
428 def putwarn(self, longm, shortm):
429 self.putx(0, -1, [8, [longm, shortm]])
430
431 def compute_crc32(self):
89b9aaf7 432 bdata = struct.pack('<H'+'I'*len(self.data), self.head & 0xffff,
ced6589f
VP
433 *tuple([d & 0xffffffff for d in self.data]))
434 return zlib.crc32(bdata)
435
436 def rec_sym(self, i, sym):
437 self.putx(i, i+5, [7, SYM_NAME[sym]])
438
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))
442 sym = DEC4B5B[v]
443 if rec:
444 self.rec_sym(i, sym)
445 return sym
446
447 def get_short(self):
448 i = self.idx
43047b89 449 # Check it's not a truncated packet.
ced6589f 450 if len(self.bits) - i <= 20:
89b9aaf7 451 self.putwarn('Truncated', '!')
ced6589f
VP
452 return 0x0BAD
453 k = [self.get_sym(i), self.get_sym(i+5),
454 self.get_sym(i+10), self.get_sym(i+15)]
43047b89 455 # TODO: Check bad symbols.
ced6589f
VP
456 val = k[0] | (k[1] << 4) | (k[2] << 8) | (k[3] << 12)
457 self.idx += 20
458 return val
459
460 def get_word(self):
461 lo = self.get_short()
462 hi = self.get_short()
463 return lo | (hi << 16)
464
465 def find_corrupted_sop(self, k):
466 # Start of packet are valid even if they have only 3 correct symbols
467 # out of 4.
f30fdbb6 468 for seq in SOP_SEQUENCES:
ced6589f
VP
469 if [k[i] == seq[i] for i in range(len(k))].count(True) >= 3:
470 return START_OF_PACKETS[seq]
471 return None
472
473 def scan_eop(self):
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))
279331bd 477 sym = START_OF_PACKETS.get(k, None)
ced6589f
VP
478 if not sym:
479 sym = self.find_corrupted_sop(k)
43047b89 480 # We have an interesting symbol sequence.
ced6589f 481 if sym:
43047b89 482 # Annotate the preamble.
ced6589f 483 self.putx(0, i, [1, ['Preamble', '...']])
43047b89 484 # Annotate each symbol.
ced6589f
VP
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':
89b9aaf7 490 self.text += 'HRST'
43047b89 491 return -1 # Hard reset
ced6589f 492 elif sym == 'Cable Reset':
89b9aaf7 493 self.text += 'CRST'
43047b89 494 return -1 # Cable reset
ced6589f
VP
495 else:
496 self.putx(i, i+20, [2, [sym, 'S']])
497 return i+20
498 self.putx(0, len(self.bits), [1, ['Junk???', 'XXX']])
89b9aaf7
VP
499 self.text += 'Junk???'
500 self.putwarn('No start of packet found', 'XXX')
43047b89 501 return -1 # No Start Of Packet
ced6589f 502
92b7b49f 503 def __init__(self):
10aeb8ea
GS
504 self.reset()
505
506 def reset(self):
ced6589f
VP
507 self.samplerate = None
508 self.idx = 0
509 self.packet_seq = 0
ced6589f 510 self.previous = 0
ced6589f
VP
511 self.startsample = None
512 self.bits = []
513 self.edges = []
514 self.bad = []
515 self.half_one = False
516 self.start_one = 0
a886ba3b 517 self.stored_pdos = {}
9d54b24e 518 self.cap_mark = [0, 0, 0, 0, 0, 0, 0, 0]
ced6589f
VP
519
520 def metadata(self, key, value):
521 if key == srd.SRD_CONF_SAMPLERATE:
522 self.samplerate = value
43047b89 523 # 0 is 2 UI, space larger than 1.5x 0 is definitely wrong.
ced6589f 524 self.maxbit = self.us2samples(3 * UI_US)
43047b89 525 # Duration threshold between half 1 and 0.
ced6589f
VP
526 self.threshold = self.us2samples(THRESHOLD_US)
527
528 def start(self):
ced6589f
VP
529 self.out_ann = self.register(srd.OUTPUT_ANN)
530 self.out_binary = self.register(srd.OUTPUT_BINARY)
531 self.out_bitrate = self.register(
532 srd.OUTPUT_META,
533 meta=(int, 'Bitrate', 'Bitrate during the packet')
534 )
535
536 def us2samples(self, us):
ced6589f
VP
537 return int(us * self.samplerate / 1000000)
538
539 def decode_packet(self):
540 self.data = []
541 self.idx = 0
89b9aaf7 542 self.text = ''
ced6589f
VP
543
544 if len(self.edges) < 50:
43047b89 545 return # Not a real PD packet
ced6589f
VP
546
547 self.packet_seq += 1
548 tstamp = float(self.startsample) / self.samplerate
89b9aaf7 549 self.text += '#%-4d (%8.6fms): ' % (self.packet_seq, tstamp*1000)
ced6589f
VP
550
551 self.idx = self.scan_eop()
552 if self.idx < 0:
43047b89 553 # Full text trace of the issue.
ced6589f 554 self.putx(0, self.idx, [12, [self.text, '...']])
43047b89 555 return # No real packet: ABORT.
ced6589f
VP
556
557 # Packet header
558 self.head = self.get_short()
559 self.putx(self.idx-20, self.idx, [3, ['H:%04x' % (self.head), 'HD']])
560 self.puthead()
561
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)
568
569 # CRC check
570 self.crc = self.get_word()
571 ccrc = self.compute_crc32()
572 if self.crc != ccrc:
89b9aaf7 573 self.putwarn('Bad CRC %08x != %08x' % (self.crc, ccrc), 'CRC!')
ced6589f
VP
574 self.putx(self.idx-40, self.idx, [5, ['CRC:%08x' % (self.crc), 'CRC']])
575
576 # End of Packet
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']])
579 self.idx += 5
580 else:
89b9aaf7 581 self.putwarn('No EOP', 'EOP!')
ced6589f
VP
582 # Full text trace
583 if self.options['fulltext'] == 'yes':
584 self.putx(0, self.idx, [12, [self.text, '...']])
585
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)
2824e811 591 self.put(es, ss, self.out_binary, [0, bytes(self.bits)])
ced6589f 592
bc6f82bb 593 def decode(self):
033e7d4d 594 if not self.samplerate:
669f30f4 595 raise SamplerateError('Cannot decode without samplerate.')
bc6f82bb 596 while True:
df167c0f 597 pins = self.wait([{0: 'e'}, {1: 'e'}, {'skip': int(self.samplerate/1e3)}])
ced6589f 598
43047b89 599 # First sample of the packet, just record the start date.
ced6589f
VP
600 if not self.startsample:
601 self.startsample = self.samplenum
602 self.previous = self.samplenum
603 continue
604
605 diff = self.samplenum - self.previous
606
43047b89 607 # Large idle: use it as the end of packet.
ced6589f 608 if diff > self.maxbit:
43047b89 609 # The last edge of the packet.
ced6589f 610 self.edges.append(self.previous)
43047b89 611 # Export the packet.
ced6589f 612 self.decode_packet()
43047b89 613 # Reset for next packet.
ced6589f
VP
614 self.startsample = self.samplenum
615 self.bits = []
616 self.edges = []
617 self.bad = []
618 self.half_one = False
619 self.start_one = 0
43047b89 620 else: # Add the bit to the packet.
ced6589f
VP
621 is_zero = diff > self.threshold
622 if is_zero and not self.half_one:
623 self.bits.append(0)
624 self.edges.append(self.previous)
625 elif not is_zero and self.half_one:
626 self.bits.append(1)
627 self.edges.append(self.start_one)
628 self.half_one = False
629 elif not is_zero and not self.half_one:
630 self.half_one = True
631 self.start_one = self.previous
43047b89 632 else: # Invalid BMC sequence
ced6589f 633 self.bad.append((self.start_one, self.previous))
43047b89 634 # TODO: Try to recover.
ced6589f
VP
635 self.bits.append(0)
636 self.edges.append(self.previous)
637 self.half_one = False
638 self.previous = self.samplenum