]> sigrok.org Git - libsigrokdecode.git/blame - decoders/spi/pd.py
spi: Don't decode data lines if CS isn't asserted
[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
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##
ad2dc0de 21
677d597b 22import sigrokdecode as srd
67e847fd 23
0702e0cf 24'''
c515eed7 25OUTPUT_PYTHON format:
0702e0cf 26
bf69977d
UH
27Packet:
28[<ptype>, <data1>, <data2>]
0702e0cf 29
bf69977d 30<ptype>:
34929ee0 31 - 'DATA': <data1> contains the MOSI data, <data2> contains the MISO data.
0702e0cf 32 The data is _usually_ 8 bits (but can also be fewer or more bits).
12549f11 33 Both data items are Python numbers (not strings), or None if the respective
6a15597a 34 channel was not supplied.
34929ee0 35 - 'BITS': <data1>/<data2> contain a list of bit values in this MOSI/MISO data
cddd11bc 36 item, and for each of those also their respective start-/endsample numbers.
0702e0cf 37 - 'CS CHANGE': <data1> is the old CS# pin value, <data2> is the new value.
1c49e875 38 Both data items are Python numbers (0/1), not strings. At the beginning of
8a110ab1
JS
39 the decoding a packet is generated with <data1> = None and <data2> being the
40 initial state of the CS# pin or None if the chip select pin is not supplied.
0702e0cf
UH
41
42Examples:
8a110ab1 43 ['CS-CHANGE', None, 1]
0702e0cf
UH
44 ['CS-CHANGE', 1, 0]
45 ['DATA', 0xff, 0x3a]
cddd11bc
UH
46 ['BITS', [[1, 80, 82], [1, 83, 84], [1, 85, 86], [1, 87, 88],
47 [1, 89, 90], [1, 91, 92], [1, 93, 94], [1, 95, 96]],
d78e0beb
UH
48 [[0, 80, 82], [1, 83, 84], [0, 85, 86], [1, 87, 88],
49 [1, 89, 90], [1, 91, 92], [0, 93, 94], [0, 95, 96]]]
0702e0cf 50 ['DATA', 0x65, 0x00]
12549f11
UH
51 ['DATA', 0xa8, None]
52 ['DATA', None, 0x55]
0702e0cf
UH
53 ['CS-CHANGE', 0, 1]
54'''
55
8a7ce2a3 56# Key: (CPOL, CPHA). Value: SPI mode.
94bbdb9a
UH
57# Clock polarity (CPOL) = 0/1: Clock is low/high when inactive.
58# Clock phase (CPHA) = 0/1: Data is valid on the leading/trailing clock edge.
c94c8c91
UH
59spi_mode = {
60 (0, 0): 0, # Mode 0
61 (0, 1): 1, # Mode 1
62 (1, 0): 2, # Mode 2
63 (1, 1): 3, # Mode 3
64}
65
37b0da68
BV
66class SamplerateError(Exception):
67 pass
68
f04964c6 69class ChannelError(Exception):
37b0da68
BV
70 pass
71
677d597b 72class Decoder(srd.Decoder):
12851357 73 api_version = 2
67e847fd 74 id = 'spi'
2b7d0e2b 75 name = 'SPI'
3d3da57d 76 longname = 'Serial Peripheral Interface'
a465436e 77 desc = 'Full-duplex, synchronous, serial bus.'
6eb87578
GM
78 license = 'gplv2+'
79 inputs = ['logic']
80 outputs = ['spi']
6a15597a 81 channels = (
49e8a4d6 82 {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
da9bcbd9 83 )
6a15597a 84 optional_channels = (
49e8a4d6
UH
85 {'id': 'miso', 'name': 'MISO', 'desc': 'Master in, slave out'},
86 {'id': 'mosi', 'name': 'MOSI', 'desc': 'Master out, slave in'},
87 {'id': 'cs', 'name': 'CS#', 'desc': 'Chip-select'},
da9bcbd9 88 )
84c1c0b5
BV
89 options = (
90 {'id': 'cs_polarity', 'desc': 'CS# polarity', 'default': 'active-low',
91 'values': ('active-low', 'active-high')},
92 {'id': 'cpol', 'desc': 'Clock polarity', 'default': 0,
93 'values': (0, 1)},
94 {'id': 'cpha', 'desc': 'Clock phase', 'default': 0,
95 'values': (0, 1)},
b0918d40 96 {'id': 'bitorder', 'desc': 'Bit order',
84c1c0b5 97 'default': 'msb-first', 'values': ('msb-first', 'lsb-first')},
b0918d40 98 {'id': 'wordsize', 'desc': 'Word size', 'default': 8},
84c1c0b5 99 )
da9bcbd9
BV
100 annotations = (
101 ('miso-data', 'MISO data'),
102 ('mosi-data', 'MOSI data'),
103 ('miso-bits', 'MISO bits'),
104 ('mosi-bits', 'MOSI bits'),
105 ('warnings', 'Human-readable warnings'),
106 )
06b52ebb 107 annotation_rows = (
cddd11bc
UH
108 ('miso-data', 'MISO data', (0,)),
109 ('miso-bits', 'MISO bits', (2,)),
110 ('mosi-data', 'MOSI data', (1,)),
111 ('mosi-bits', 'MOSI bits', (3,)),
112 ('other', 'Other', (4,)),
06b52ebb 113 )
49d0e05c
UH
114 binary = (
115 ('miso', 'MISO'),
116 ('mosi', 'MOSI'),
117 )
6eb87578 118
3643fc3f 119 def __init__(self):
8a3c8792 120 self.samplerate = None
bcd14870 121 self.oldclk = 1
a10bfc48 122 self.bitcount = 0
bbc100f7 123 self.misodata = self.mosidata = 0
cddd11bc 124 self.misobits = []
bbc100f7 125 self.mosibits = []
486b19ce 126 self.ss_block = -1
d6bace96 127 self.samplenum = -1
bb08f4b3 128 self.cs_was_deasserted = False
8a110ab1 129 self.oldcs = None
2fcd7c22 130 self.oldpins = None
bbc100f7 131 self.have_cs = self.have_miso = self.have_mosi = None
1c49e875 132 self.no_cs_notification = False
6eb87578 133
8a3c8792
BV
134 def metadata(self, key, value):
135 if key == srd.SRD_CONF_SAMPLERATE:
136 self.samplerate = value
137
8915b346 138 def start(self):
c515eed7 139 self.out_python = self.register(srd.OUTPUT_PYTHON)
be465111 140 self.out_ann = self.register(srd.OUTPUT_ANN)
49d0e05c 141 self.out_bin = self.register(srd.OUTPUT_BINARY)
8a3c8792
BV
142 self.out_bitrate = self.register(srd.OUTPUT_META,
143 meta=(int, 'Bitrate', 'Bitrate during transfers'))
3643fc3f 144
ec0afe27 145 def putw(self, data):
486b19ce 146 self.put(self.ss_block, self.samplenum, self.out_ann, data)
ec0afe27 147
bbc100f7
UH
148 def putdata(self):
149 # Pass MISO and MOSI bits and then data to the next PD up the stack.
150 so = self.misodata if self.have_miso else None
151 si = self.mosidata if self.have_mosi else None
152 so_bits = self.misobits if self.have_miso else None
153 si_bits = self.mosibits if self.have_mosi else None
7c09dbb2
UH
154
155 if self.have_miso:
156 ss, es = self.misobits[-1][1], self.misobits[0][2]
49d0e05c 157 self.put(ss, es, self.out_bin, (0, bytes([so])))
7c09dbb2
UH
158 if self.have_mosi:
159 ss, es = self.mosibits[-1][1], self.mosibits[0][2]
49d0e05c 160 self.put(ss, es, self.out_bin, (1, bytes([si])))
7c09dbb2
UH
161
162 self.put(ss, es, self.out_python, ['BITS', si_bits, so_bits])
163 self.put(ss, es, self.out_python, ['DATA', si, so])
cddd11bc 164
bbc100f7
UH
165 # Bit annotations.
166 if self.have_miso:
167 for bit in self.misobits:
168 self.put(bit[1], bit[2], self.out_ann, [2, ['%d' % bit[0]]])
169 if self.have_mosi:
170 for bit in self.mosibits:
171 self.put(bit[1], bit[2], self.out_ann, [3, ['%d' % bit[0]]])
172
173 # Dataword annotations.
174 if self.have_miso:
808c6e74 175 self.put(ss, es, self.out_ann, [0, ['%02X' % self.misodata]])
bbc100f7 176 if self.have_mosi:
808c6e74 177 self.put(ss, es, self.out_ann, [1, ['%02X' % self.mosidata]])
cddd11bc 178
d482a2d3
UH
179 def reset_decoder_state(self):
180 self.misodata = 0 if self.have_miso else None
181 self.mosidata = 0 if self.have_mosi else None
182 self.misobits = [] if self.have_miso else None
183 self.mosibits = [] if self.have_mosi else None
184 self.bitcount = 0
185
cdceaa94
AS
186 def cs_asserted(self, cs):
187 active_low = (self.options['cs_polarity'] == 'active-low')
188 return (cs == 0) if active_low else (cs == 1)
189
bcd14870 190 def handle_bit(self, miso, mosi, clk, cs):
cddd11bc 191 # If this is the first bit of a dataword, save its sample number.
191ec8c5 192 if self.bitcount == 0:
486b19ce 193 self.ss_block = self.samplenum
cdceaa94
AS
194 self.cs_was_deasserted = \
195 not self.cs_asserted(cs) if self.have_cs else False
2fcd7c22 196
191ec8c5 197 ws = self.options['wordsize']
d6bace96 198
bbc100f7
UH
199 # Receive MISO bit into our shift register.
200 if self.have_miso:
201 if self.options['bitorder'] == 'msb-first':
202 self.misodata |= miso << (ws - 1 - self.bitcount)
203 else:
204 self.misodata |= miso << self.bitcount
205
191ec8c5 206 # Receive MOSI bit into our shift register.
12549f11
UH
207 if self.have_mosi:
208 if self.options['bitorder'] == 'msb-first':
209 self.mosidata |= mosi << (ws - 1 - self.bitcount)
210 else:
211 self.mosidata |= mosi << self.bitcount
3e3c0330 212
808c6e74 213 # Guesstimate the endsample for this bit (can be overridden below).
bbc100f7
UH
214 es = self.samplenum
215 if self.bitcount > 0:
8e8096e8
UH
216 if self.have_miso:
217 es += self.samplenum - self.misobits[0][1]
218 elif self.have_mosi:
219 es += self.samplenum - self.mosibits[0][1]
c94c8c91 220
cddd11bc 221 if self.have_miso:
d78e0beb 222 self.misobits.insert(0, [miso, self.samplenum, es])
cddd11bc 223 if self.have_mosi:
d78e0beb 224 self.mosibits.insert(0, [mosi, self.samplenum, es])
bbc100f7
UH
225
226 if self.bitcount > 0 and self.have_miso:
d78e0beb 227 self.misobits[1][2] = self.samplenum
bbc100f7 228 if self.bitcount > 0 and self.have_mosi:
d78e0beb 229 self.mosibits[1][2] = self.samplenum
cddd11bc 230
191ec8c5 231 self.bitcount += 1
1ea831e9 232
191ec8c5
UH
233 # Continue to receive if not enough bits were received, yet.
234 if self.bitcount != ws:
235 return
b1bb5eed 236
bbc100f7 237 self.putdata()
12549f11
UH
238
239 # Meta bitrate.
bbc100f7 240 elapsed = 1 / float(self.samplerate)
486b19ce 241 elapsed *= (self.samplenum - self.ss_block + 1)
8a3c8792 242 bitrate = int(1 / elapsed * self.options['wordsize'])
486b19ce 243 self.put(self.ss_block, self.samplenum, self.out_bitrate, bitrate)
8a3c8792 244
bb08f4b3 245 if self.have_cs and self.cs_was_deasserted:
cddd11bc 246 self.putw([4, ['CS# was deasserted during this data word!']])
191ec8c5 247
d482a2d3 248 self.reset_decoder_state()
191ec8c5 249
bcd14870 250 def find_clk_edge(self, miso, mosi, clk, cs):
efa64173 251 if self.have_cs and self.oldcs != cs:
191ec8c5 252 # Send all CS# pin value changes.
c515eed7 253 self.put(self.samplenum, self.samplenum, self.out_python,
191ec8c5
UH
254 ['CS-CHANGE', self.oldcs, cs])
255 self.oldcs = cs
efa64173 256 # Reset decoder state when CS# changes (and the CS# pin is used).
d482a2d3 257 self.reset_decoder_state()
191ec8c5 258
cdceaa94
AS
259 # We only care about samples if CS# is asserted.
260 if self.have_cs and not self.cs_asserted(cs):
261 return
262
191ec8c5 263 # Ignore sample if the clock pin hasn't changed.
bcd14870 264 if clk == self.oldclk:
191ec8c5 265 return
b1bb5eed 266
bcd14870 267 self.oldclk = clk
b1bb5eed 268
191ec8c5
UH
269 # Sample data on rising/falling clock edge (depends on mode).
270 mode = spi_mode[self.options['cpol'], self.options['cpha']]
bcd14870 271 if mode == 0 and clk == 0: # Sample on rising clock edge
191ec8c5 272 return
bcd14870 273 elif mode == 1 and clk == 1: # Sample on falling clock edge
191ec8c5 274 return
bcd14870 275 elif mode == 2 and clk == 1: # Sample on falling clock edge
191ec8c5 276 return
bcd14870 277 elif mode == 3 and clk == 0: # Sample on rising clock edge
191ec8c5
UH
278 return
279
280 # Found the correct clock edge, now get the SPI bit(s).
bcd14870 281 self.handle_bit(miso, mosi, clk, cs)
191ec8c5
UH
282
283 def decode(self, ss, es, data):
37b0da68 284 if not self.samplerate:
21cda951 285 raise SamplerateError('Cannot decode without samplerate.')
12549f11 286 # Either MISO or MOSI can be omitted (but not both). CS# is optional.
191ec8c5 287 for (self.samplenum, pins) in data:
01329e88 288
191ec8c5
UH
289 # Ignore identical samples early on (for performance reasons).
290 if self.oldpins == pins:
291 continue
bcd14870 292 self.oldpins, (clk, miso, mosi, cs) = pins, pins
12549f11
UH
293 self.have_miso = (miso in (0, 1))
294 self.have_mosi = (mosi in (0, 1))
efa64173 295 self.have_cs = (cs in (0, 1))
b1bb5eed 296
9ed11500
UH
297 # Either MISO or MOSI (but not both) can be omitted.
298 if not (self.have_miso or self.have_mosi):
f04964c6 299 raise ChannelError('Either MISO or MOSI (or both) pins required.')
9ed11500 300
1c49e875
JS
301 # Tell stacked decoders that we don't have a CS# signal.
302 if not self.no_cs_notification and not self.have_cs:
8a110ab1 303 self.put(0, 0, self.out_python, ['CS-CHANGE', None, None])
1c49e875
JS
304 self.no_cs_notification = True
305
4fecc5a4 306 self.find_clk_edge(miso, mosi, clk, cs)