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