]> sigrok.org Git - libsigrokdecode.git/blame - decoders/spi/pd.py
spi: Add support for "transfer" annotations.
[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
f04964c6 73class ChannelError(Exception):
37b0da68
BV
74 pass
75
677d597b 76class Decoder(srd.Decoder):
15814cab 77 api_version = 3
67e847fd 78 id = 'spi'
2b7d0e2b 79 name = 'SPI'
3d3da57d 80 longname = 'Serial Peripheral Interface'
a465436e 81 desc = 'Full-duplex, synchronous, serial bus.'
6eb87578
GM
82 license = 'gplv2+'
83 inputs = ['logic']
84 outputs = ['spi']
d6d8a8a4 85 tags = ['Embedded/industrial']
6a15597a 86 channels = (
49e8a4d6 87 {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
da9bcbd9 88 )
6a15597a 89 optional_channels = (
49e8a4d6
UH
90 {'id': 'miso', 'name': 'MISO', 'desc': 'Master in, slave out'},
91 {'id': 'mosi', 'name': 'MOSI', 'desc': 'Master out, slave in'},
92 {'id': 'cs', 'name': 'CS#', 'desc': 'Chip-select'},
da9bcbd9 93 )
84c1c0b5
BV
94 options = (
95 {'id': 'cs_polarity', 'desc': 'CS# polarity', 'default': 'active-low',
96 'values': ('active-low', 'active-high')},
97 {'id': 'cpol', 'desc': 'Clock polarity', 'default': 0,
98 'values': (0, 1)},
99 {'id': 'cpha', 'desc': 'Clock phase', 'default': 0,
100 'values': (0, 1)},
b0918d40 101 {'id': 'bitorder', 'desc': 'Bit order',
84c1c0b5 102 'default': 'msb-first', 'values': ('msb-first', 'lsb-first')},
b0918d40 103 {'id': 'wordsize', 'desc': 'Word size', 'default': 8},
84c1c0b5 104 )
da9bcbd9
BV
105 annotations = (
106 ('miso-data', 'MISO data'),
107 ('mosi-data', 'MOSI data'),
108 ('miso-bits', 'MISO bits'),
109 ('mosi-bits', 'MOSI bits'),
110 ('warnings', 'Human-readable warnings'),
a69b0f0b
UH
111 ('miso-transfer', 'MISO transfer'),
112 ('mosi-transfer', 'MOSI transfer'),
da9bcbd9 113 )
06b52ebb 114 annotation_rows = (
cddd11bc
UH
115 ('miso-data', 'MISO data', (0,)),
116 ('miso-bits', 'MISO bits', (2,)),
a69b0f0b 117 ('miso-transfer', 'MISO transfer', (5,)),
cddd11bc
UH
118 ('mosi-data', 'MOSI data', (1,)),
119 ('mosi-bits', 'MOSI bits', (3,)),
a69b0f0b 120 ('mosi-transfer', 'MOSI transfer', (6,)),
cddd11bc 121 ('other', 'Other', (4,)),
06b52ebb 122 )
49d0e05c
UH
123 binary = (
124 ('miso', 'MISO'),
125 ('mosi', 'MOSI'),
126 )
6eb87578 127
3643fc3f 128 def __init__(self):
10aeb8ea
GS
129 self.reset()
130
131 def reset(self):
8a3c8792 132 self.samplerate = None
a10bfc48 133 self.bitcount = 0
bbc100f7 134 self.misodata = self.mosidata = 0
cddd11bc 135 self.misobits = []
bbc100f7 136 self.mosibits = []
8c90d7bd
PLE
137 self.misobytes = []
138 self.mosibytes = []
486b19ce 139 self.ss_block = -1
d6bace96 140 self.samplenum = -1
8c90d7bd 141 self.ss_transfer = -1
bb08f4b3 142 self.cs_was_deasserted = False
bbc100f7 143 self.have_cs = self.have_miso = self.have_mosi = None
6eb87578 144
8915b346 145 def start(self):
c515eed7 146 self.out_python = self.register(srd.OUTPUT_PYTHON)
be465111 147 self.out_ann = self.register(srd.OUTPUT_ANN)
2f370328 148 self.out_binary = self.register(srd.OUTPUT_BINARY)
f04db50a
JG
149 self.out_bitrate = self.register(srd.OUTPUT_META,
150 meta=(int, 'Bitrate', 'Bitrate during transfers'))
8284ca73 151 self.bw = (self.options['wordsize'] + 7) // 8
3643fc3f 152
f04db50a
JG
153 def metadata(self, key, value):
154 if key == srd.SRD_CONF_SAMPLERATE:
155 self.samplerate = value
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']
d399c641 217 bo = self.options['bitorder']
d6bace96 218
bbc100f7
UH
219 # Receive MISO bit into our shift register.
220 if self.have_miso:
d399c641 221 if bo == 'msb-first':
bbc100f7
UH
222 self.misodata |= miso << (ws - 1 - self.bitcount)
223 else:
224 self.misodata |= miso << self.bitcount
225
191ec8c5 226 # Receive MOSI bit into our shift register.
12549f11 227 if self.have_mosi:
d399c641 228 if bo == 'msb-first':
12549f11
UH
229 self.mosidata |= mosi << (ws - 1 - self.bitcount)
230 else:
231 self.mosidata |= mosi << self.bitcount
3e3c0330 232
808c6e74 233 # Guesstimate the endsample for this bit (can be overridden below).
bbc100f7
UH
234 es = self.samplenum
235 if self.bitcount > 0:
8e8096e8
UH
236 if self.have_miso:
237 es += self.samplenum - self.misobits[0][1]
238 elif self.have_mosi:
239 es += self.samplenum - self.mosibits[0][1]
c94c8c91 240
cddd11bc 241 if self.have_miso:
d78e0beb 242 self.misobits.insert(0, [miso, self.samplenum, es])
cddd11bc 243 if self.have_mosi:
d78e0beb 244 self.mosibits.insert(0, [mosi, self.samplenum, es])
bbc100f7
UH
245
246 if self.bitcount > 0 and self.have_miso:
d78e0beb 247 self.misobits[1][2] = self.samplenum
bbc100f7 248 if self.bitcount > 0 and self.have_mosi:
d78e0beb 249 self.mosibits[1][2] = self.samplenum
cddd11bc 250
191ec8c5 251 self.bitcount += 1
1ea831e9 252
191ec8c5
UH
253 # Continue to receive if not enough bits were received, yet.
254 if self.bitcount != ws:
255 return
b1bb5eed 256
bbc100f7 257 self.putdata()
12549f11
UH
258
259 # Meta bitrate.
956721de 260 if self.samplerate:
fa7fdc54
GS
261 elapsed = 1 / float(self.samplerate)
262 elapsed *= (self.samplenum - self.ss_block + 1)
d399c641 263 bitrate = int(1 / elapsed * ws)
fa7fdc54 264 self.put(self.ss_block, self.samplenum, self.out_bitrate, bitrate)
8a3c8792 265
bb08f4b3 266 if self.have_cs and self.cs_was_deasserted:
cddd11bc 267 self.putw([4, ['CS# was deasserted during this data word!']])
191ec8c5 268
d482a2d3 269 self.reset_decoder_state()
191ec8c5 270
de15aa2a
GS
271 def find_clk_edge(self, miso, mosi, clk, cs, first):
272 if self.have_cs and (first or self.matched[self.have_cs]):
191ec8c5 273 # Send all CS# pin value changes.
de15aa2a 274 oldcs = None if first else 1 - cs
c515eed7 275 self.put(self.samplenum, self.samplenum, self.out_python,
de15aa2a 276 ['CS-CHANGE', oldcs, cs])
8c90d7bd
PLE
277
278 if self.cs_asserted(cs):
279 self.ss_transfer = self.samplenum
280 self.misobytes = []
281 self.mosibytes = []
282 else:
a69b0f0b
UH
283 if self.have_miso:
284 self.put(self.ss_transfer, self.samplenum, self.out_ann,
285 [5, [' '.join(format(x.val, '02X') for x in self.misobytes)]])
286 if self.have_mosi:
287 self.put(self.ss_transfer, self.samplenum, self.out_ann,
288 [6, [' '.join(format(x.val, '02X') for x in self.mosibytes)]])
8c90d7bd
PLE
289 self.put(self.ss_transfer, self.samplenum, self.out_python,
290 ['TRANSFER', self.mosibytes, self.misobytes])
291
efa64173 292 # Reset decoder state when CS# changes (and the CS# pin is used).
d482a2d3 293 self.reset_decoder_state()
191ec8c5 294
cdceaa94
AS
295 # We only care about samples if CS# is asserted.
296 if self.have_cs and not self.cs_asserted(cs):
297 return
298
191ec8c5 299 # Ignore sample if the clock pin hasn't changed.
de15aa2a 300 if first or not self.matched[0]:
191ec8c5 301 return
b1bb5eed 302
191ec8c5
UH
303 # Sample data on rising/falling clock edge (depends on mode).
304 mode = spi_mode[self.options['cpol'], self.options['cpha']]
bcd14870 305 if mode == 0 and clk == 0: # Sample on rising clock edge
191ec8c5 306 return
bcd14870 307 elif mode == 1 and clk == 1: # Sample on falling clock edge
191ec8c5 308 return
bcd14870 309 elif mode == 2 and clk == 1: # Sample on falling clock edge
191ec8c5 310 return
bcd14870 311 elif mode == 3 and clk == 0: # Sample on rising clock edge
191ec8c5
UH
312 return
313
314 # Found the correct clock edge, now get the SPI bit(s).
bcd14870 315 self.handle_bit(miso, mosi, clk, cs)
191ec8c5 316
15814cab 317 def decode(self):
de15aa2a
GS
318 # The CLK input is mandatory. Other signals are (individually)
319 # optional. Yet either MISO or MOSI (or both) must be provided.
320 # Tell stacked decoders when we don't have a CS# signal.
321 if not self.has_channel(0):
322 raise ChannelError('Either MISO or MOSI (or both) pins required.')
15814cab
GS
323 self.have_miso = self.has_channel(1)
324 self.have_mosi = self.has_channel(2)
15814cab
GS
325 if not self.have_miso and not self.have_mosi:
326 raise ChannelError('Either MISO or MOSI (or both) pins required.')
de15aa2a 327 self.have_cs = self.has_channel(3)
15814cab
GS
328 if not self.have_cs:
329 self.put(0, 0, self.out_python, ['CS-CHANGE', None, None])
330
de15aa2a
GS
331 # We want all CLK changes. We want all CS changes if CS is used.
332 # Map 'have_cs' from boolean to an integer index. This simplifies
333 # evaluation in other locations.
334 wait_cond = [{0: 'e'}]
335 if self.have_cs:
336 self.have_cs = len(wait_cond)
337 wait_cond.append({3: 'e'})
338
15814cab
GS
339 # "Pixel compatibility" with the v2 implementation. Grab and
340 # process the very first sample before checking for edges. The
de15aa2a
GS
341 # previous implementation did this by seeding old values with
342 # None, which led to an immediate "change" in comparison.
d399c641 343 (clk, miso, mosi, cs) = self.wait({})
de15aa2a 344 self.find_clk_edge(miso, mosi, clk, cs, True)
15814cab
GS
345
346 while True:
d399c641 347 (clk, miso, mosi, cs) = self.wait(wait_cond)
de15aa2a 348 self.find_clk_edge(miso, mosi, clk, cs, False)