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