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