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