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