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