]> sigrok.org Git - libsigrokdecode.git/blame - decoders/spi/pd.py
spi: Missing samplerate spec is not fatal
[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):
8a3c8792 124 self.samplerate = None
a10bfc48 125 self.bitcount = 0
bbc100f7 126 self.misodata = self.mosidata = 0
cddd11bc 127 self.misobits = []
bbc100f7 128 self.mosibits = []
8c90d7bd
PLE
129 self.misobytes = []
130 self.mosibytes = []
486b19ce 131 self.ss_block = -1
d6bace96 132 self.samplenum = -1
8c90d7bd 133 self.ss_transfer = -1
bb08f4b3 134 self.cs_was_deasserted = False
bbc100f7 135 self.have_cs = self.have_miso = self.have_mosi = None
6eb87578 136
8a3c8792
BV
137 def metadata(self, key, value):
138 if key == srd.SRD_CONF_SAMPLERATE:
139 self.samplerate = value
140
8915b346 141 def start(self):
c515eed7 142 self.out_python = self.register(srd.OUTPUT_PYTHON)
be465111 143 self.out_ann = self.register(srd.OUTPUT_ANN)
2f370328 144 self.out_binary = self.register(srd.OUTPUT_BINARY)
fa7fdc54
GS
145 if self.samplerate is not None:
146 self.out_bitrate = self.register(srd.OUTPUT_META,
147 meta=(int, 'Bitrate', 'Bitrate during transfers'))
8284ca73 148 self.bw = (self.options['wordsize'] + 7) // 8
3643fc3f 149
ec0afe27 150 def putw(self, data):
486b19ce 151 self.put(self.ss_block, self.samplenum, self.out_ann, data)
ec0afe27 152
bbc100f7
UH
153 def putdata(self):
154 # Pass MISO and MOSI bits and then data to the next PD up the stack.
155 so = self.misodata if self.have_miso else None
156 si = self.mosidata if self.have_mosi else None
157 so_bits = self.misobits if self.have_miso else None
158 si_bits = self.mosibits if self.have_mosi else None
7c09dbb2
UH
159
160 if self.have_miso:
161 ss, es = self.misobits[-1][1], self.misobits[0][2]
8284ca73
UH
162 bdata = so.to_bytes(self.bw, byteorder='big')
163 self.put(ss, es, self.out_binary, [0, bdata])
7c09dbb2
UH
164 if self.have_mosi:
165 ss, es = self.mosibits[-1][1], self.mosibits[0][2]
8284ca73
UH
166 bdata = si.to_bytes(self.bw, byteorder='big')
167 self.put(ss, es, self.out_binary, [1, bdata])
7c09dbb2
UH
168
169 self.put(ss, es, self.out_python, ['BITS', si_bits, so_bits])
170 self.put(ss, es, self.out_python, ['DATA', si, so])
cddd11bc 171
8c90d7bd
PLE
172 if self.have_miso:
173 self.misobytes.append(Data(ss=ss, es=es, val=so))
174 if self.have_mosi:
175 self.mosibytes.append(Data(ss=ss, es=es, val=si))
176
bbc100f7
UH
177 # Bit annotations.
178 if self.have_miso:
179 for bit in self.misobits:
180 self.put(bit[1], bit[2], self.out_ann, [2, ['%d' % bit[0]]])
181 if self.have_mosi:
182 for bit in self.mosibits:
183 self.put(bit[1], bit[2], self.out_ann, [3, ['%d' % bit[0]]])
184
185 # Dataword annotations.
186 if self.have_miso:
808c6e74 187 self.put(ss, es, self.out_ann, [0, ['%02X' % self.misodata]])
bbc100f7 188 if self.have_mosi:
808c6e74 189 self.put(ss, es, self.out_ann, [1, ['%02X' % self.mosidata]])
cddd11bc 190
d482a2d3
UH
191 def reset_decoder_state(self):
192 self.misodata = 0 if self.have_miso else None
193 self.mosidata = 0 if self.have_mosi else None
194 self.misobits = [] if self.have_miso else None
195 self.mosibits = [] if self.have_mosi else None
196 self.bitcount = 0
197
cdceaa94
AS
198 def cs_asserted(self, cs):
199 active_low = (self.options['cs_polarity'] == 'active-low')
200 return (cs == 0) if active_low else (cs == 1)
201
bcd14870 202 def handle_bit(self, miso, mosi, clk, cs):
cddd11bc 203 # If this is the first bit of a dataword, save its sample number.
191ec8c5 204 if self.bitcount == 0:
486b19ce 205 self.ss_block = self.samplenum
cdceaa94
AS
206 self.cs_was_deasserted = \
207 not self.cs_asserted(cs) if self.have_cs else False
2fcd7c22 208
191ec8c5 209 ws = self.options['wordsize']
d6bace96 210
bbc100f7
UH
211 # Receive MISO bit into our shift register.
212 if self.have_miso:
213 if self.options['bitorder'] == 'msb-first':
214 self.misodata |= miso << (ws - 1 - self.bitcount)
215 else:
216 self.misodata |= miso << self.bitcount
217
191ec8c5 218 # Receive MOSI bit into our shift register.
12549f11
UH
219 if self.have_mosi:
220 if self.options['bitorder'] == 'msb-first':
221 self.mosidata |= mosi << (ws - 1 - self.bitcount)
222 else:
223 self.mosidata |= mosi << self.bitcount
3e3c0330 224
808c6e74 225 # Guesstimate the endsample for this bit (can be overridden below).
bbc100f7
UH
226 es = self.samplenum
227 if self.bitcount > 0:
8e8096e8
UH
228 if self.have_miso:
229 es += self.samplenum - self.misobits[0][1]
230 elif self.have_mosi:
231 es += self.samplenum - self.mosibits[0][1]
c94c8c91 232
cddd11bc 233 if self.have_miso:
d78e0beb 234 self.misobits.insert(0, [miso, self.samplenum, es])
cddd11bc 235 if self.have_mosi:
d78e0beb 236 self.mosibits.insert(0, [mosi, self.samplenum, es])
bbc100f7
UH
237
238 if self.bitcount > 0 and self.have_miso:
d78e0beb 239 self.misobits[1][2] = self.samplenum
bbc100f7 240 if self.bitcount > 0 and self.have_mosi:
d78e0beb 241 self.mosibits[1][2] = self.samplenum
cddd11bc 242
191ec8c5 243 self.bitcount += 1
1ea831e9 244
191ec8c5
UH
245 # Continue to receive if not enough bits were received, yet.
246 if self.bitcount != ws:
247 return
b1bb5eed 248
bbc100f7 249 self.putdata()
12549f11
UH
250
251 # Meta bitrate.
fa7fdc54
GS
252 if self.samplerate is not None:
253 elapsed = 1 / float(self.samplerate)
254 elapsed *= (self.samplenum - self.ss_block + 1)
255 bitrate = int(1 / elapsed * self.options['wordsize'])
256 self.put(self.ss_block, self.samplenum, self.out_bitrate, bitrate)
8a3c8792 257
bb08f4b3 258 if self.have_cs and self.cs_was_deasserted:
cddd11bc 259 self.putw([4, ['CS# was deasserted during this data word!']])
191ec8c5 260
d482a2d3 261 self.reset_decoder_state()
191ec8c5 262
de15aa2a
GS
263 def find_clk_edge(self, miso, mosi, clk, cs, first):
264 if self.have_cs and (first or self.matched[self.have_cs]):
191ec8c5 265 # Send all CS# pin value changes.
de15aa2a 266 oldcs = None if first else 1 - cs
c515eed7 267 self.put(self.samplenum, self.samplenum, self.out_python,
de15aa2a 268 ['CS-CHANGE', oldcs, cs])
8c90d7bd
PLE
269
270 if self.cs_asserted(cs):
271 self.ss_transfer = self.samplenum
272 self.misobytes = []
273 self.mosibytes = []
274 else:
275 self.put(self.ss_transfer, self.samplenum, self.out_python,
276 ['TRANSFER', self.mosibytes, self.misobytes])
277
efa64173 278 # Reset decoder state when CS# changes (and the CS# pin is used).
d482a2d3 279 self.reset_decoder_state()
191ec8c5 280
cdceaa94
AS
281 # We only care about samples if CS# is asserted.
282 if self.have_cs and not self.cs_asserted(cs):
283 return
284
191ec8c5 285 # Ignore sample if the clock pin hasn't changed.
de15aa2a 286 if first or not self.matched[0]:
191ec8c5 287 return
b1bb5eed 288
191ec8c5
UH
289 # Sample data on rising/falling clock edge (depends on mode).
290 mode = spi_mode[self.options['cpol'], self.options['cpha']]
bcd14870 291 if mode == 0 and clk == 0: # Sample on rising clock edge
191ec8c5 292 return
bcd14870 293 elif mode == 1 and clk == 1: # Sample on falling clock edge
191ec8c5 294 return
bcd14870 295 elif mode == 2 and clk == 1: # Sample on falling clock edge
191ec8c5 296 return
bcd14870 297 elif mode == 3 and clk == 0: # Sample on rising clock edge
191ec8c5
UH
298 return
299
300 # Found the correct clock edge, now get the SPI bit(s).
bcd14870 301 self.handle_bit(miso, mosi, clk, cs)
191ec8c5 302
15814cab 303 def decode(self):
de15aa2a
GS
304 # The CLK input is mandatory. Other signals are (individually)
305 # optional. Yet either MISO or MOSI (or both) must be provided.
306 # Tell stacked decoders when we don't have a CS# signal.
307 if not self.has_channel(0):
308 raise ChannelError('Either MISO or MOSI (or both) pins required.')
15814cab
GS
309 self.have_miso = self.has_channel(1)
310 self.have_mosi = self.has_channel(2)
15814cab
GS
311 if not self.have_miso and not self.have_mosi:
312 raise ChannelError('Either MISO or MOSI (or both) pins required.')
de15aa2a 313 self.have_cs = self.has_channel(3)
15814cab
GS
314 if not self.have_cs:
315 self.put(0, 0, self.out_python, ['CS-CHANGE', None, None])
316
de15aa2a
GS
317 # We want all CLK changes. We want all CS changes if CS is used.
318 # Map 'have_cs' from boolean to an integer index. This simplifies
319 # evaluation in other locations.
320 wait_cond = [{0: 'e'}]
321 if self.have_cs:
322 self.have_cs = len(wait_cond)
323 wait_cond.append({3: 'e'})
324
15814cab
GS
325 # "Pixel compatibility" with the v2 implementation. Grab and
326 # process the very first sample before checking for edges. The
de15aa2a
GS
327 # previous implementation did this by seeding old values with
328 # None, which led to an immediate "change" in comparison.
15814cab
GS
329 pins = self.wait({})
330 (clk, miso, mosi, cs) = pins
de15aa2a 331 self.find_clk_edge(miso, mosi, clk, cs, True)
15814cab
GS
332
333 while True:
191ec8c5 334 # Ignore identical samples early on (for performance reasons).
de15aa2a 335 pins = self.wait(wait_cond)
15814cab 336 (clk, miso, mosi, cs) = pins
de15aa2a 337 self.find_clk_edge(miso, mosi, clk, cs, False)