]> sigrok.org Git - libsigrokdecode.git/blame - decoders/spi/pd.py
spi: Improve use of PD API version 3
[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
37b0da68
BV
73class SamplerateError(Exception):
74 pass
75
f04964c6 76class ChannelError(Exception):
37b0da68
BV
77 pass
78
677d597b 79class Decoder(srd.Decoder):
15814cab 80 api_version = 3
67e847fd 81 id = 'spi'
2b7d0e2b 82 name = 'SPI'
3d3da57d 83 longname = 'Serial Peripheral Interface'
a465436e 84 desc = 'Full-duplex, synchronous, serial bus.'
6eb87578
GM
85 license = 'gplv2+'
86 inputs = ['logic']
87 outputs = ['spi']
6a15597a 88 channels = (
49e8a4d6 89 {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
da9bcbd9 90 )
6a15597a 91 optional_channels = (
49e8a4d6
UH
92 {'id': 'miso', 'name': 'MISO', 'desc': 'Master in, slave out'},
93 {'id': 'mosi', 'name': 'MOSI', 'desc': 'Master out, slave in'},
94 {'id': 'cs', 'name': 'CS#', 'desc': 'Chip-select'},
da9bcbd9 95 )
84c1c0b5
BV
96 options = (
97 {'id': 'cs_polarity', 'desc': 'CS# polarity', 'default': 'active-low',
98 'values': ('active-low', 'active-high')},
99 {'id': 'cpol', 'desc': 'Clock polarity', 'default': 0,
100 'values': (0, 1)},
101 {'id': 'cpha', 'desc': 'Clock phase', 'default': 0,
102 'values': (0, 1)},
b0918d40 103 {'id': 'bitorder', 'desc': 'Bit order',
84c1c0b5 104 'default': 'msb-first', 'values': ('msb-first', 'lsb-first')},
b0918d40 105 {'id': 'wordsize', 'desc': 'Word size', 'default': 8},
84c1c0b5 106 )
da9bcbd9
BV
107 annotations = (
108 ('miso-data', 'MISO data'),
109 ('mosi-data', 'MOSI data'),
110 ('miso-bits', 'MISO bits'),
111 ('mosi-bits', 'MOSI bits'),
112 ('warnings', 'Human-readable warnings'),
113 )
06b52ebb 114 annotation_rows = (
cddd11bc
UH
115 ('miso-data', 'MISO data', (0,)),
116 ('miso-bits', 'MISO bits', (2,)),
117 ('mosi-data', 'MOSI data', (1,)),
118 ('mosi-bits', 'MOSI bits', (3,)),
119 ('other', 'Other', (4,)),
06b52ebb 120 )
49d0e05c
UH
121 binary = (
122 ('miso', 'MISO'),
123 ('mosi', 'MOSI'),
124 )
6eb87578 125
3643fc3f 126 def __init__(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)
8a3c8792
BV
148 self.out_bitrate = self.register(srd.OUTPUT_META,
149 meta=(int, 'Bitrate', 'Bitrate during transfers'))
8284ca73 150 self.bw = (self.options['wordsize'] + 7) // 8
3643fc3f 151
ec0afe27 152 def putw(self, data):
486b19ce 153 self.put(self.ss_block, self.samplenum, self.out_ann, data)
ec0afe27 154
bbc100f7
UH
155 def putdata(self):
156 # Pass MISO and MOSI bits and then data to the next PD up the stack.
157 so = self.misodata if self.have_miso else None
158 si = self.mosidata if self.have_mosi else None
159 so_bits = self.misobits if self.have_miso else None
160 si_bits = self.mosibits if self.have_mosi else None
7c09dbb2
UH
161
162 if self.have_miso:
163 ss, es = self.misobits[-1][1], self.misobits[0][2]
8284ca73
UH
164 bdata = so.to_bytes(self.bw, byteorder='big')
165 self.put(ss, es, self.out_binary, [0, bdata])
7c09dbb2
UH
166 if self.have_mosi:
167 ss, es = self.mosibits[-1][1], self.mosibits[0][2]
8284ca73
UH
168 bdata = si.to_bytes(self.bw, byteorder='big')
169 self.put(ss, es, self.out_binary, [1, bdata])
7c09dbb2
UH
170
171 self.put(ss, es, self.out_python, ['BITS', si_bits, so_bits])
172 self.put(ss, es, self.out_python, ['DATA', si, so])
cddd11bc 173
8c90d7bd
PLE
174 if self.have_miso:
175 self.misobytes.append(Data(ss=ss, es=es, val=so))
176 if self.have_mosi:
177 self.mosibytes.append(Data(ss=ss, es=es, val=si))
178
bbc100f7
UH
179 # Bit annotations.
180 if self.have_miso:
181 for bit in self.misobits:
182 self.put(bit[1], bit[2], self.out_ann, [2, ['%d' % bit[0]]])
183 if self.have_mosi:
184 for bit in self.mosibits:
185 self.put(bit[1], bit[2], self.out_ann, [3, ['%d' % bit[0]]])
186
187 # Dataword annotations.
188 if self.have_miso:
808c6e74 189 self.put(ss, es, self.out_ann, [0, ['%02X' % self.misodata]])
bbc100f7 190 if self.have_mosi:
808c6e74 191 self.put(ss, es, self.out_ann, [1, ['%02X' % self.mosidata]])
cddd11bc 192
d482a2d3
UH
193 def reset_decoder_state(self):
194 self.misodata = 0 if self.have_miso else None
195 self.mosidata = 0 if self.have_mosi else None
196 self.misobits = [] if self.have_miso else None
197 self.mosibits = [] if self.have_mosi else None
198 self.bitcount = 0
199
cdceaa94
AS
200 def cs_asserted(self, cs):
201 active_low = (self.options['cs_polarity'] == 'active-low')
202 return (cs == 0) if active_low else (cs == 1)
203
bcd14870 204 def handle_bit(self, miso, mosi, clk, cs):
cddd11bc 205 # If this is the first bit of a dataword, save its sample number.
191ec8c5 206 if self.bitcount == 0:
486b19ce 207 self.ss_block = self.samplenum
cdceaa94
AS
208 self.cs_was_deasserted = \
209 not self.cs_asserted(cs) if self.have_cs else False
2fcd7c22 210
191ec8c5 211 ws = self.options['wordsize']
d6bace96 212
bbc100f7
UH
213 # Receive MISO bit into our shift register.
214 if self.have_miso:
215 if self.options['bitorder'] == 'msb-first':
216 self.misodata |= miso << (ws - 1 - self.bitcount)
217 else:
218 self.misodata |= miso << self.bitcount
219
191ec8c5 220 # Receive MOSI bit into our shift register.
12549f11
UH
221 if self.have_mosi:
222 if self.options['bitorder'] == 'msb-first':
223 self.mosidata |= mosi << (ws - 1 - self.bitcount)
224 else:
225 self.mosidata |= mosi << self.bitcount
3e3c0330 226
808c6e74 227 # Guesstimate the endsample for this bit (can be overridden below).
bbc100f7
UH
228 es = self.samplenum
229 if self.bitcount > 0:
8e8096e8
UH
230 if self.have_miso:
231 es += self.samplenum - self.misobits[0][1]
232 elif self.have_mosi:
233 es += self.samplenum - self.mosibits[0][1]
c94c8c91 234
cddd11bc 235 if self.have_miso:
d78e0beb 236 self.misobits.insert(0, [miso, self.samplenum, es])
cddd11bc 237 if self.have_mosi:
d78e0beb 238 self.mosibits.insert(0, [mosi, self.samplenum, es])
bbc100f7
UH
239
240 if self.bitcount > 0 and self.have_miso:
d78e0beb 241 self.misobits[1][2] = self.samplenum
bbc100f7 242 if self.bitcount > 0 and self.have_mosi:
d78e0beb 243 self.mosibits[1][2] = self.samplenum
cddd11bc 244
191ec8c5 245 self.bitcount += 1
1ea831e9 246
191ec8c5
UH
247 # Continue to receive if not enough bits were received, yet.
248 if self.bitcount != ws:
249 return
b1bb5eed 250
bbc100f7 251 self.putdata()
12549f11
UH
252
253 # Meta bitrate.
bbc100f7 254 elapsed = 1 / float(self.samplerate)
486b19ce 255 elapsed *= (self.samplenum - self.ss_block + 1)
8a3c8792 256 bitrate = int(1 / elapsed * self.options['wordsize'])
486b19ce 257 self.put(self.ss_block, self.samplenum, self.out_bitrate, bitrate)
8a3c8792 258
bb08f4b3 259 if self.have_cs and self.cs_was_deasserted:
cddd11bc 260 self.putw([4, ['CS# was deasserted during this data word!']])
191ec8c5 261
d482a2d3 262 self.reset_decoder_state()
191ec8c5 263
de15aa2a
GS
264 def find_clk_edge(self, miso, mosi, clk, cs, first):
265 if self.have_cs and (first or self.matched[self.have_cs]):
191ec8c5 266 # Send all CS# pin value changes.
de15aa2a 267 oldcs = None if first else 1 - cs
c515eed7 268 self.put(self.samplenum, self.samplenum, self.out_python,
de15aa2a 269 ['CS-CHANGE', oldcs, cs])
8c90d7bd
PLE
270
271 if self.cs_asserted(cs):
272 self.ss_transfer = self.samplenum
273 self.misobytes = []
274 self.mosibytes = []
275 else:
276 self.put(self.ss_transfer, self.samplenum, self.out_python,
277 ['TRANSFER', self.mosibytes, self.misobytes])
278
efa64173 279 # Reset decoder state when CS# changes (and the CS# pin is used).
d482a2d3 280 self.reset_decoder_state()
191ec8c5 281
cdceaa94
AS
282 # We only care about samples if CS# is asserted.
283 if self.have_cs and not self.cs_asserted(cs):
284 return
285
191ec8c5 286 # Ignore sample if the clock pin hasn't changed.
de15aa2a 287 if first or not self.matched[0]:
191ec8c5 288 return
b1bb5eed 289
191ec8c5
UH
290 # Sample data on rising/falling clock edge (depends on mode).
291 mode = spi_mode[self.options['cpol'], self.options['cpha']]
bcd14870 292 if mode == 0 and clk == 0: # Sample on rising clock edge
191ec8c5 293 return
bcd14870 294 elif mode == 1 and clk == 1: # Sample on falling clock edge
191ec8c5 295 return
bcd14870 296 elif mode == 2 and clk == 1: # Sample on falling clock edge
191ec8c5 297 return
bcd14870 298 elif mode == 3 and clk == 0: # Sample on rising clock edge
191ec8c5
UH
299 return
300
301 # Found the correct clock edge, now get the SPI bit(s).
bcd14870 302 self.handle_bit(miso, mosi, clk, cs)
191ec8c5 303
15814cab 304 def decode(self):
37b0da68 305 if not self.samplerate:
21cda951 306 raise SamplerateError('Cannot decode without samplerate.')
01329e88 307
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.
15814cab
GS
333 pins = self.wait({})
334 (clk, miso, mosi, cs) = pins
de15aa2a 335 self.find_clk_edge(miso, mosi, clk, cs, True)
15814cab
GS
336
337 while True:
191ec8c5 338 # Ignore identical samples early on (for performance reasons).
de15aa2a 339 pins = self.wait(wait_cond)
15814cab 340 (clk, miso, mosi, cs) = pins
de15aa2a 341 self.find_clk_edge(miso, mosi, clk, cs, False)