]> sigrok.org Git - libsigrokdecode.git/blame - decoders/usb_signalling/pd.py
usb_signalling: Document protocol output format.
[libsigrokdecode.git] / decoders / usb_signalling / pd.py
CommitLineData
2dc6d41c 1##
50bd5d25 2## This file is part of the libsigrokdecode project.
2dc6d41c
UH
3##
4## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
7d4b5fac 5## Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de>
2dc6d41c
UH
6##
7## This program is free software; you can redistribute it and/or modify
8## it under the terms of the GNU General Public License as published by
9## the Free Software Foundation; either version 2 of the License, or
10## (at your option) any later version.
11##
12## This program is distributed in the hope that it will be useful,
13## but WITHOUT ANY WARRANTY; without even the implied warranty of
14## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15## GNU General Public License for more details.
16##
17## You should have received a copy of the GNU General Public License
18## along with this program; if not, write to the Free Software
19## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20##
21
22# USB signalling (low-speed and full-speed) protocol decoder
23
24import sigrokdecode as srd
25
a56b8fe1
UH
26'''
27Protocol output format:
28
29Packet:
30[<ptype>, <pdata>]
31
32<ptype>, <pdata>:
33 - 'SOP', None
34 - 'SYM', <sym>
35 - 'BIT', <bit>
36 - 'STUFF BIT', None
37 - 'EOP', None
38 - 'PACKET', <packet>
39
40<sym>:
41 - 'J', 'K', 'SE0', or 'SE1'
42
43<bit>:
44 - 0 or 1
45 - Note: Symbols like SE0, SE1, and the J that's part of EOP don't yield 'BIT'.
46
47<packet>:
48 - A string consisting of '1' and '0' characters, e.g. '11010100'.
49 - The packet contains only "real" bits, no stuff bits (and no SOF/EOP).
50'''
51
d1970f14 52# Low-/full-speed symbols.
2dc6d41c 53# Note: Low-speed J and K are inverted compared to the full-speed J and K!
7dc75721
UH
54symbols = {
55 'low-speed': {
2dc6d41c
UH
56 # (<dp>, <dm>): <symbol/state>
57 (0, 0): 'SE0',
58 (1, 0): 'K',
59 (0, 1): 'J',
60 (1, 1): 'SE1',
7dc75721
UH
61 },
62 'full-speed': {
2dc6d41c
UH
63 # (<dp>, <dm>): <symbol/state>
64 (0, 0): 'SE0',
65 (1, 0): 'J',
66 (0, 1): 'K',
67 (1, 1): 'SE1',
7dc75721 68 },
2dc6d41c
UH
69}
70
5bb55598
UH
71bitrates = {
72 'low-speed': 1500000, # 1.5Mb/s (+/- 1.5%)
73 'full-speed': 12000000, # 12Mb/s (+/- 0.25%)
74}
75
2dc6d41c
UH
76class Decoder(srd.Decoder):
77 api_version = 1
78 id = 'usb_signalling'
79 name = 'USB signalling'
80 longname = 'Universal Serial Bus (LS/FS) signalling'
9e1437a0 81 desc = 'USB (low-speed and full-speed) signalling protocol.'
2dc6d41c
UH
82 license = 'gplv2+'
83 inputs = ['logic']
84 outputs = ['usb_signalling']
85 probes = [
86 {'id': 'dp', 'name': 'D+', 'desc': 'USB D+ signal'},
87 {'id': 'dm', 'name': 'D-', 'desc': 'USB D- signal'},
88 ]
89 optional_probes = []
90 options = {
91 'signalling': ['Signalling', 'full-speed'],
92 }
93 annotations = [
edad8134
UH
94 ['symbol', 'Symbol'],
95 ['sop', 'Start of packet (SOP)'],
96 ['eop', 'End of packet (EOP)'],
97 ['bit', 'Bit'],
98 ['stuffbit', 'Stuff bit'],
99 ['packet', 'Packet'],
2dc6d41c
UH
100 ]
101
102 def __init__(self):
d1970f14 103 self.oldsym = 'J' # The "idle" state is J.
fdd5ee5e
UH
104 self.ss_sop = None
105 self.ss_block = None
2dc6d41c 106 self.samplenum = 0
2dc6d41c
UH
107 self.packet = ''
108 self.syms = []
5bb55598
UH
109 self.bitrate = None
110 self.bitwidth = None
d1970f14
UH
111 self.bitnum = 0
112 self.samplenum_target = None
2fcd7c22 113 self.oldpins = None
d1970f14
UH
114 self.consecutive_ones = 0
115 self.state = 'IDLE'
2dc6d41c
UH
116
117 def start(self, metadata):
2dc6d41c
UH
118 self.out_proto = self.add(srd.OUTPUT_PROTO, 'usb_signalling')
119 self.out_ann = self.add(srd.OUTPUT_ANN, 'usb_signalling')
5bb55598
UH
120 self.bitrate = bitrates[self.options['signalling']]
121 self.bitwidth = float(metadata['samplerate']) / float(self.bitrate)
fdd5ee5e 122 self.halfbit = int(self.bitwidth / 2)
2dc6d41c
UH
123
124 def report(self):
125 pass
126
d1970f14 127 def putpx(self, data):
7d4b5fac
UH
128 self.put(self.samplenum, self.samplenum, self.out_proto, data)
129
130 def putx(self, data):
131 self.put(self.samplenum, self.samplenum, self.out_ann, data)
132
fdd5ee5e
UH
133 def putpm(self, data):
134 s, h = self.samplenum, self.halfbit
135 self.put(self.ss_block - h, self.samplenum + h, self.out_proto, data)
136
137 def putm(self, data):
138 s, h = self.samplenum, self.halfbit
139 self.put(self.ss_block - h, self.samplenum + h, self.out_ann, data)
140
d1970f14 141 def putpb(self, data):
fdd5ee5e
UH
142 s, h = self.samplenum, self.halfbit
143 self.put(s - h, s + h, self.out_proto, data)
d1970f14
UH
144
145 def putb(self, data):
fdd5ee5e
UH
146 s, h = self.samplenum, self.halfbit
147 self.put(s - h, s + h, self.out_ann, data)
d1970f14
UH
148
149 def set_new_target_samplenum(self):
150 bitpos = self.ss_sop + (self.bitwidth / 2)
151 bitpos += self.bitnum * self.bitwidth
152 self.samplenum_target = int(bitpos)
153
154 def wait_for_sop(self, sym):
155 # Wait for a Start of Packet (SOP), i.e. a J->K symbol change.
156 if sym != 'K':
157 self.oldsym = sym
158 return
159 self.ss_sop = self.samplenum
160 self.set_new_target_samplenum()
161 self.putpx(['SOP', None])
edad8134 162 self.putx([1, ['SOP']])
d1970f14
UH
163 self.state = 'GET BIT'
164
165 def handle_bit(self, sym, b):
166 if self.consecutive_ones == 6 and b == '0':
167 # Stuff bit. Don't add to the packet, reset self.consecutive_ones.
a56b8fe1 168 self.putpb(['STUFF BIT', None])
edad8134 169 self.putb([4, ['SB: %s/%s' % (sym, b)]])
d1970f14
UH
170 self.consecutive_ones = 0
171 else:
172 # Normal bit. Add it to the packet, update self.consecutive_ones.
a56b8fe1 173 self.putpb(['BIT', b])
edad8134 174 self.putb([3, ['%s/%s' % (sym, b)]])
d1970f14
UH
175 self.packet += b
176 if b == '1':
177 self.consecutive_ones += 1
178 else:
179 self.consecutive_ones = 0
180
181 def get_eop(self, sym):
182 # EOP: SE0 for >= 1 bittime (usually 2 bittimes), then J.
183 self.syms.append(sym)
184 self.putpb(['SYM', sym])
185 self.putb([0, ['%s' % sym]])
186 self.bitnum += 1
187 self.set_new_target_samplenum()
188 self.oldsym = sym
189 if self.syms[-2:] == ['SE0', 'J']:
190 # Got an EOP, i.e. we now have a full packet.
fdd5ee5e
UH
191 self.putpm(['EOP', None])
192 self.putm([2, ['EOP']])
ab96960e
UH
193 self.ss_block = self.ss_sop
194 self.putpm(['PACKET', self.packet])
195 self.putm([5, ['PACKET: %s' % self.packet]])
d1970f14
UH
196 self.bitnum, self.packet, self.syms, self.state = 0, '', [], 'IDLE'
197 self.consecutive_ones = 0
198
199 def get_bit(self, sym):
200 if sym == 'SE0':
201 # Start of an EOP. Change state, run get_eop() for this bit.
202 self.state = 'GET EOP'
fdd5ee5e 203 self.ss_block = self.samplenum
d1970f14
UH
204 self.get_eop(sym)
205 return
206 self.syms.append(sym)
207 self.putpb(['SYM', sym])
208 b = '0' if self.oldsym != sym else '1'
209 self.handle_bit(sym, b)
210 self.bitnum += 1
211 self.set_new_target_samplenum()
212 self.oldsym = sym
213
2dc6d41c 214 def decode(self, ss, es, data):
2fcd7c22 215 for (self.samplenum, pins) in data:
d1970f14
UH
216 # State machine.
217 if self.state == 'IDLE':
218 # Ignore identical samples early on (for performance reasons).
219 if self.oldpins == pins:
220 continue
221 self.oldpins = pins
222 sym = symbols[self.options['signalling']][tuple(pins)]
223 self.wait_for_sop(sym)
224 elif self.state in ('GET BIT', 'GET EOP'):
225 # Wait until we're in the middle of the desired bit.
226 if self.samplenum < self.samplenum_target:
227 continue
228 sym = symbols[self.options['signalling']][tuple(pins)]
229 if self.state == 'GET BIT':
230 self.get_bit(sym)
231 elif self.state == 'GET EOP':
232 self.get_eop(sym)
2dc6d41c 233 else:
d1970f14 234 raise Exception('Invalid state: %s' % self.state)
2dc6d41c 235