]> sigrok.org Git - libsigrokdecode.git/blame - decoders/spi/pd.py
atsha204a: Shorten a few more code chunks, add helpers.
[libsigrokdecode.git] / decoders / spi / pd.py
CommitLineData
6eb87578 1##
50bd5d25 2## This file is part of the libsigrokdecode project.
6eb87578
GM
3##
4## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
12549f11 5## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
6eb87578
GM
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
4539e9ca 18## along with this program; if not, see <http://www.gnu.org/licenses/>.
6eb87578 19##
ad2dc0de 20
677d597b 21import sigrokdecode as srd
8c90d7bd
PLE
22from collections import namedtuple
23
24Data = namedtuple('Data', ['ss', 'es', 'val'])
67e847fd 25
0702e0cf 26'''
c515eed7 27OUTPUT_PYTHON format:
0702e0cf 28
bf69977d
UH
29Packet:
30[<ptype>, <data1>, <data2>]
0702e0cf 31
bf69977d 32<ptype>:
34929ee0 33 - 'DATA': <data1> contains the MOSI data, <data2> contains the MISO data.
0702e0cf 34 The data is _usually_ 8 bits (but can also be fewer or more bits).
12549f11 35 Both data items are Python numbers (not strings), or None if the respective
6a15597a 36 channel was not supplied.
34929ee0 37 - 'BITS': <data1>/<data2> contain a list of bit values in this MOSI/MISO data
cddd11bc 38 item, and for each of those also their respective start-/endsample numbers.
92a06d0b 39 - 'CS-CHANGE': <data1> is the old CS# pin value, <data2> is the new value.
1c49e875 40 Both data items are Python numbers (0/1), not strings. At the beginning of
8a110ab1
JS
41 the decoding a packet is generated with <data1> = None and <data2> being the
42 initial state of the CS# pin or None if the chip select pin is not supplied.
8c90d7bd
PLE
43 - 'TRANSFER': <data1>/<data2> contain a list of Data() namedtuples for each
44 byte transferred during this block of CS# asserted time. Each Data() has
45 fields ss, es, and val.
0702e0cf
UH
46
47Examples:
8a110ab1 48 ['CS-CHANGE', None, 1]
0702e0cf
UH
49 ['CS-CHANGE', 1, 0]
50 ['DATA', 0xff, 0x3a]
cddd11bc
UH
51 ['BITS', [[1, 80, 82], [1, 83, 84], [1, 85, 86], [1, 87, 88],
52 [1, 89, 90], [1, 91, 92], [1, 93, 94], [1, 95, 96]],
d78e0beb
UH
53 [[0, 80, 82], [1, 83, 84], [0, 85, 86], [1, 87, 88],
54 [1, 89, 90], [1, 91, 92], [0, 93, 94], [0, 95, 96]]]
0702e0cf 55 ['DATA', 0x65, 0x00]
12549f11
UH
56 ['DATA', 0xa8, None]
57 ['DATA', None, 0x55]
0702e0cf 58 ['CS-CHANGE', 0, 1]
8c90d7bd
PLE
59 ['TRANSFER', [Data(ss=80, es=96, val=0xff), ...],
60 [Data(ss=80, es=96, val=0x3a), ...]]
0702e0cf
UH
61'''
62
8a7ce2a3 63# Key: (CPOL, CPHA). Value: SPI mode.
94bbdb9a
UH
64# Clock polarity (CPOL) = 0/1: Clock is low/high when inactive.
65# Clock phase (CPHA) = 0/1: Data is valid on the leading/trailing clock edge.
c94c8c91
UH
66spi_mode = {
67 (0, 0): 0, # Mode 0
68 (0, 1): 1, # Mode 1
69 (1, 0): 2, # Mode 2
70 (1, 1): 3, # Mode 3
71}
72
f04964c6 73class ChannelError(Exception):
37b0da68
BV
74 pass
75
677d597b 76class Decoder(srd.Decoder):
15814cab 77 api_version = 3
67e847fd 78 id = 'spi'
2b7d0e2b 79 name = 'SPI'
3d3da57d 80 longname = 'Serial Peripheral Interface'
a465436e 81 desc = 'Full-duplex, synchronous, serial bus.'
6eb87578
GM
82 license = 'gplv2+'
83 inputs = ['logic']
84 outputs = ['spi']
6a15597a 85 channels = (
49e8a4d6 86 {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
da9bcbd9 87 )
6a15597a 88 optional_channels = (
49e8a4d6
UH
89 {'id': 'miso', 'name': 'MISO', 'desc': 'Master in, slave out'},
90 {'id': 'mosi', 'name': 'MOSI', 'desc': 'Master out, slave in'},
91 {'id': 'cs', 'name': 'CS#', 'desc': 'Chip-select'},
da9bcbd9 92 )
84c1c0b5
BV
93 options = (
94 {'id': 'cs_polarity', 'desc': 'CS# polarity', 'default': 'active-low',
95 'values': ('active-low', 'active-high')},
96 {'id': 'cpol', 'desc': 'Clock polarity', 'default': 0,
97 'values': (0, 1)},
98 {'id': 'cpha', 'desc': 'Clock phase', 'default': 0,
99 'values': (0, 1)},
b0918d40 100 {'id': 'bitorder', 'desc': 'Bit order',
84c1c0b5 101 'default': 'msb-first', 'values': ('msb-first', 'lsb-first')},
b0918d40 102 {'id': 'wordsize', 'desc': 'Word size', 'default': 8},
84c1c0b5 103 )
da9bcbd9
BV
104 annotations = (
105 ('miso-data', 'MISO data'),
106 ('mosi-data', 'MOSI data'),
107 ('miso-bits', 'MISO bits'),
108 ('mosi-bits', 'MOSI bits'),
109 ('warnings', 'Human-readable warnings'),
110 )
06b52ebb 111 annotation_rows = (
cddd11bc
UH
112 ('miso-data', 'MISO data', (0,)),
113 ('miso-bits', 'MISO bits', (2,)),
114 ('mosi-data', 'MOSI data', (1,)),
115 ('mosi-bits', 'MOSI bits', (3,)),
116 ('other', 'Other', (4,)),
06b52ebb 117 )
49d0e05c
UH
118 binary = (
119 ('miso', 'MISO'),
120 ('mosi', 'MOSI'),
121 )
6eb87578 122
3643fc3f 123 def __init__(self):
10aeb8ea
GS
124 self.reset()
125
126 def reset(self):
8a3c8792 127 self.samplerate = None
a10bfc48 128 self.bitcount = 0
bbc100f7 129 self.misodata = self.mosidata = 0
cddd11bc 130 self.misobits = []
bbc100f7 131 self.mosibits = []
8c90d7bd
PLE
132 self.misobytes = []
133 self.mosibytes = []
486b19ce 134 self.ss_block = -1
d6bace96 135 self.samplenum = -1
8c90d7bd 136 self.ss_transfer = -1
bb08f4b3 137 self.cs_was_deasserted = False
bbc100f7 138 self.have_cs = self.have_miso = self.have_mosi = None
6eb87578 139
8a3c8792
BV
140 def metadata(self, key, value):
141 if key == srd.SRD_CONF_SAMPLERATE:
142 self.samplerate = value
143
8915b346 144 def start(self):
c515eed7 145 self.out_python = self.register(srd.OUTPUT_PYTHON)
be465111 146 self.out_ann = self.register(srd.OUTPUT_ANN)
2f370328 147 self.out_binary = self.register(srd.OUTPUT_BINARY)
956721de 148 if self.samplerate:
fa7fdc54
GS
149 self.out_bitrate = self.register(srd.OUTPUT_META,
150 meta=(int, 'Bitrate', 'Bitrate during transfers'))
8284ca73 151 self.bw = (self.options['wordsize'] + 7) // 8
3643fc3f 152
ec0afe27 153 def putw(self, data):
486b19ce 154 self.put(self.ss_block, self.samplenum, self.out_ann, data)
ec0afe27 155
bbc100f7
UH
156 def putdata(self):
157 # Pass MISO and MOSI bits and then data to the next PD up the stack.
158 so = self.misodata if self.have_miso else None
159 si = self.mosidata if self.have_mosi else None
160 so_bits = self.misobits if self.have_miso else None
161 si_bits = self.mosibits if self.have_mosi else None
7c09dbb2
UH
162
163 if self.have_miso:
164 ss, es = self.misobits[-1][1], self.misobits[0][2]
8284ca73
UH
165 bdata = so.to_bytes(self.bw, byteorder='big')
166 self.put(ss, es, self.out_binary, [0, bdata])
7c09dbb2
UH
167 if self.have_mosi:
168 ss, es = self.mosibits[-1][1], self.mosibits[0][2]
8284ca73
UH
169 bdata = si.to_bytes(self.bw, byteorder='big')
170 self.put(ss, es, self.out_binary, [1, bdata])
7c09dbb2
UH
171
172 self.put(ss, es, self.out_python, ['BITS', si_bits, so_bits])
173 self.put(ss, es, self.out_python, ['DATA', si, so])
cddd11bc 174
8c90d7bd
PLE
175 if self.have_miso:
176 self.misobytes.append(Data(ss=ss, es=es, val=so))
177 if self.have_mosi:
178 self.mosibytes.append(Data(ss=ss, es=es, val=si))
179
bbc100f7
UH
180 # Bit annotations.
181 if self.have_miso:
182 for bit in self.misobits:
183 self.put(bit[1], bit[2], self.out_ann, [2, ['%d' % bit[0]]])
184 if self.have_mosi:
185 for bit in self.mosibits:
186 self.put(bit[1], bit[2], self.out_ann, [3, ['%d' % bit[0]]])
187
188 # Dataword annotations.
189 if self.have_miso:
808c6e74 190 self.put(ss, es, self.out_ann, [0, ['%02X' % self.misodata]])
bbc100f7 191 if self.have_mosi:
808c6e74 192 self.put(ss, es, self.out_ann, [1, ['%02X' % self.mosidata]])
cddd11bc 193
d482a2d3
UH
194 def reset_decoder_state(self):
195 self.misodata = 0 if self.have_miso else None
196 self.mosidata = 0 if self.have_mosi else None
197 self.misobits = [] if self.have_miso else None
198 self.mosibits = [] if self.have_mosi else None
199 self.bitcount = 0
200
cdceaa94
AS
201 def cs_asserted(self, cs):
202 active_low = (self.options['cs_polarity'] == 'active-low')
203 return (cs == 0) if active_low else (cs == 1)
204
bcd14870 205 def handle_bit(self, miso, mosi, clk, cs):
cddd11bc 206 # If this is the first bit of a dataword, save its sample number.
191ec8c5 207 if self.bitcount == 0:
486b19ce 208 self.ss_block = self.samplenum
cdceaa94
AS
209 self.cs_was_deasserted = \
210 not self.cs_asserted(cs) if self.have_cs else False
2fcd7c22 211
191ec8c5 212 ws = self.options['wordsize']
d399c641 213 bo = self.options['bitorder']
d6bace96 214
bbc100f7
UH
215 # Receive MISO bit into our shift register.
216 if self.have_miso:
d399c641 217 if bo == 'msb-first':
bbc100f7
UH
218 self.misodata |= miso << (ws - 1 - self.bitcount)
219 else:
220 self.misodata |= miso << self.bitcount
221
191ec8c5 222 # Receive MOSI bit into our shift register.
12549f11 223 if self.have_mosi:
d399c641 224 if bo == 'msb-first':
12549f11
UH
225 self.mosidata |= mosi << (ws - 1 - self.bitcount)
226 else:
227 self.mosidata |= mosi << self.bitcount
3e3c0330 228
808c6e74 229 # Guesstimate the endsample for this bit (can be overridden below).
bbc100f7
UH
230 es = self.samplenum
231 if self.bitcount > 0:
8e8096e8
UH
232 if self.have_miso:
233 es += self.samplenum - self.misobits[0][1]
234 elif self.have_mosi:
235 es += self.samplenum - self.mosibits[0][1]
c94c8c91 236
cddd11bc 237 if self.have_miso:
d78e0beb 238 self.misobits.insert(0, [miso, self.samplenum, es])
cddd11bc 239 if self.have_mosi:
d78e0beb 240 self.mosibits.insert(0, [mosi, self.samplenum, es])
bbc100f7
UH
241
242 if self.bitcount > 0 and self.have_miso:
d78e0beb 243 self.misobits[1][2] = self.samplenum
bbc100f7 244 if self.bitcount > 0 and self.have_mosi:
d78e0beb 245 self.mosibits[1][2] = self.samplenum
cddd11bc 246
191ec8c5 247 self.bitcount += 1
1ea831e9 248
191ec8c5
UH
249 # Continue to receive if not enough bits were received, yet.
250 if self.bitcount != ws:
251 return
b1bb5eed 252
bbc100f7 253 self.putdata()
12549f11
UH
254
255 # Meta bitrate.
956721de 256 if self.samplerate:
fa7fdc54
GS
257 elapsed = 1 / float(self.samplerate)
258 elapsed *= (self.samplenum - self.ss_block + 1)
d399c641 259 bitrate = int(1 / elapsed * ws)
fa7fdc54 260 self.put(self.ss_block, self.samplenum, self.out_bitrate, bitrate)
8a3c8792 261
bb08f4b3 262 if self.have_cs and self.cs_was_deasserted:
cddd11bc 263 self.putw([4, ['CS# was deasserted during this data word!']])
191ec8c5 264
d482a2d3 265 self.reset_decoder_state()
191ec8c5 266
de15aa2a
GS
267 def find_clk_edge(self, miso, mosi, clk, cs, first):
268 if self.have_cs and (first or self.matched[self.have_cs]):
191ec8c5 269 # Send all CS# pin value changes.
de15aa2a 270 oldcs = None if first else 1 - cs
c515eed7 271 self.put(self.samplenum, self.samplenum, self.out_python,
de15aa2a 272 ['CS-CHANGE', oldcs, cs])
8c90d7bd
PLE
273
274 if self.cs_asserted(cs):
275 self.ss_transfer = self.samplenum
276 self.misobytes = []
277 self.mosibytes = []
278 else:
279 self.put(self.ss_transfer, self.samplenum, self.out_python,
280 ['TRANSFER', self.mosibytes, self.misobytes])
281
efa64173 282 # Reset decoder state when CS# changes (and the CS# pin is used).
d482a2d3 283 self.reset_decoder_state()
191ec8c5 284
cdceaa94
AS
285 # We only care about samples if CS# is asserted.
286 if self.have_cs and not self.cs_asserted(cs):
287 return
288
191ec8c5 289 # Ignore sample if the clock pin hasn't changed.
de15aa2a 290 if first or not self.matched[0]:
191ec8c5 291 return
b1bb5eed 292
191ec8c5
UH
293 # Sample data on rising/falling clock edge (depends on mode).
294 mode = spi_mode[self.options['cpol'], self.options['cpha']]
bcd14870 295 if mode == 0 and clk == 0: # Sample on rising clock edge
191ec8c5 296 return
bcd14870 297 elif mode == 1 and clk == 1: # Sample on falling clock edge
191ec8c5 298 return
bcd14870 299 elif mode == 2 and clk == 1: # Sample on falling clock edge
191ec8c5 300 return
bcd14870 301 elif mode == 3 and clk == 0: # Sample on rising clock edge
191ec8c5
UH
302 return
303
304 # Found the correct clock edge, now get the SPI bit(s).
bcd14870 305 self.handle_bit(miso, mosi, clk, cs)
191ec8c5 306
15814cab 307 def decode(self):
de15aa2a
GS
308 # The CLK input is mandatory. Other signals are (individually)
309 # optional. Yet either MISO or MOSI (or both) must be provided.
310 # Tell stacked decoders when we don't have a CS# signal.
311 if not self.has_channel(0):
312 raise ChannelError('Either MISO or MOSI (or both) pins required.')
15814cab
GS
313 self.have_miso = self.has_channel(1)
314 self.have_mosi = self.has_channel(2)
15814cab
GS
315 if not self.have_miso and not self.have_mosi:
316 raise ChannelError('Either MISO or MOSI (or both) pins required.')
de15aa2a 317 self.have_cs = self.has_channel(3)
15814cab
GS
318 if not self.have_cs:
319 self.put(0, 0, self.out_python, ['CS-CHANGE', None, None])
320
de15aa2a
GS
321 # We want all CLK changes. We want all CS changes if CS is used.
322 # Map 'have_cs' from boolean to an integer index. This simplifies
323 # evaluation in other locations.
324 wait_cond = [{0: 'e'}]
325 if self.have_cs:
326 self.have_cs = len(wait_cond)
327 wait_cond.append({3: 'e'})
328
15814cab
GS
329 # "Pixel compatibility" with the v2 implementation. Grab and
330 # process the very first sample before checking for edges. The
de15aa2a
GS
331 # previous implementation did this by seeding old values with
332 # None, which led to an immediate "change" in comparison.
d399c641 333 (clk, miso, mosi, cs) = self.wait({})
de15aa2a 334 self.find_clk_edge(miso, mosi, clk, cs, True)
15814cab
GS
335
336 while True:
d399c641 337 (clk, miso, mosi, cs) = self.wait(wait_cond)
de15aa2a 338 self.find_clk_edge(miso, mosi, clk, cs, False)