]> sigrok.org Git - libsigrokdecode.git/blame - decoders/spi/pd.py
spi: Output per-bit annotations and OUTPUT_PYTHON data.
[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
UH
26
27SPI packet:
28[<cmd>, <data1>, <data2>]
29
30Commands:
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
UH
33 Both data items are Python numbers (not strings), or None if the respective
34 probe 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]],
45 [[0, 80, 82], [0, 83, 84], [1, 85, 86], [1, 87, 88],
46 [1, 89, 90], [0, 91, 92], [1, 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):
a2c2afd9 64 api_version = 1
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']
6b5b91d2 72 probes = [
49e8a4d6 73 {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
6b5b91d2 74 ]
efa64173 75 optional_probes = [
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'},
efa64173 79 ]
238b4080 80 options = {
94bbdb9a
UH
81 'cs_polarity': ['CS# polarity', 'active-low'],
82 'cpol': ['Clock polarity', 0],
83 'cpha': ['Clock phase', 0],
84 'bitorder': ['Bit order within the SPI data', 'msb-first'],
c94c8c91 85 'wordsize': ['Word size of SPI data', 8], # 1-64?
3eda7779 86 'format': ['Data format', 'hex'],
238b4080 87 }
b1bb5eed 88 annotations = [
cddd11bc
UH
89 ['miso-data', 'MISO data'],
90 ['mosi-data', 'MOSI data'],
91 ['miso-bits', 'MISO bits'],
92 ['mosi-bits', 'MOSI bits'],
9f2f42c0 93 ['warnings', 'Human-readable warnings'],
b1bb5eed 94 ]
06b52ebb 95 annotation_rows = (
cddd11bc
UH
96 ('miso-data', 'MISO data', (0,)),
97 ('miso-bits', 'MISO bits', (2,)),
98 ('mosi-data', 'MOSI data', (1,)),
99 ('mosi-bits', 'MOSI bits', (3,)),
100 ('other', 'Other', (4,)),
06b52ebb 101 )
6eb87578 102
3643fc3f 103 def __init__(self):
8a3c8792 104 self.samplerate = None
bcd14870 105 self.oldclk = 1
a10bfc48 106 self.bitcount = 0
4917bb31 107 self.mosidata = 0
d6bace96 108 self.misodata = 0
cddd11bc
UH
109 self.mosibits = []
110 self.misobits = []
ec0afe27 111 self.startsample = -1
d6bace96 112 self.samplenum = -1
01329e88 113 self.cs_was_deasserted_during_data_word = 0
3e3c0330 114 self.oldcs = -1
2fcd7c22 115 self.oldpins = None
12549f11
UH
116 self.have_cs = None
117 self.have_miso = None
118 self.have_mosi = None
191ec8c5 119 self.state = 'IDLE'
6eb87578 120
8a3c8792
BV
121 def metadata(self, key, value):
122 if key == srd.SRD_CONF_SAMPLERATE:
123 self.samplerate = value
124
8915b346 125 def start(self):
c515eed7 126 self.out_python = self.register(srd.OUTPUT_PYTHON)
be465111 127 self.out_ann = self.register(srd.OUTPUT_ANN)
8a3c8792
BV
128 self.out_bitrate = self.register(srd.OUTPUT_META,
129 meta=(int, 'Bitrate', 'Bitrate during transfers'))
3643fc3f 130
ec0afe27 131 def putpw(self, data):
c515eed7 132 self.put(self.startsample, self.samplenum, self.out_python, data)
ec0afe27
UH
133
134 def putw(self, data):
135 self.put(self.startsample, self.samplenum, self.out_ann, data)
136
cddd11bc
UH
137 def putmisobit(self, i, data):
138 self.put(self.misobits[i][1], self.misobits[i][2], self.out_ann, data)
139
140 def putmosibit(self, i, data):
141 self.put(self.mosibits[i][1], self.mosibits[i][2], self.out_ann, data)
142
bcd14870 143 def handle_bit(self, miso, mosi, clk, cs):
cddd11bc 144 # If this is the first bit of a dataword, save its sample number.
191ec8c5
UH
145 if self.bitcount == 0:
146 self.startsample = self.samplenum
efa64173
UH
147 if self.have_cs:
148 active_low = (self.options['cs_polarity'] == 'active-low')
149 deasserted = cs if active_low else not cs
150 if deasserted:
151 self.cs_was_deasserted_during_data_word = 1
2fcd7c22 152
191ec8c5 153 ws = self.options['wordsize']
d6bace96 154
191ec8c5 155 # Receive MOSI bit into our shift register.
12549f11
UH
156 if self.have_mosi:
157 if self.options['bitorder'] == 'msb-first':
158 self.mosidata |= mosi << (ws - 1 - self.bitcount)
159 else:
160 self.mosidata |= mosi << self.bitcount
3e3c0330 161
191ec8c5 162 # Receive MISO bit into our shift register.
12549f11
UH
163 if self.have_miso:
164 if self.options['bitorder'] == 'msb-first':
165 self.misodata |= miso << (ws - 1 - self.bitcount)
166 else:
167 self.misodata |= miso << self.bitcount
c94c8c91 168
cddd11bc
UH
169 if self.have_miso:
170 self.misobits.append([miso, self.samplenum, -1])
171 if self.have_mosi:
172 self.mosibits.append([mosi, self.samplenum, -1])
173 if self.bitcount != 0:
174 if self.have_miso:
175 self.misobits[self.bitcount - 1][2] = self.samplenum
176 self.putmisobit(self.bitcount - 1, [3, ['%d' % miso]])
177 if self.have_mosi:
178 self.mosibits[self.bitcount - 1][2] = self.samplenum
179 self.putmosibit(self.bitcount - 1, [2, ['%d' % mosi]])
180
191ec8c5 181 self.bitcount += 1
1ea831e9 182
191ec8c5
UH
183 # Continue to receive if not enough bits were received, yet.
184 if self.bitcount != ws:
185 return
b1bb5eed 186
12549f11
UH
187 si = self.mosidata if self.have_mosi else None
188 so = self.misodata if self.have_miso else None
cddd11bc
UH
189 si_bits = self.mosibits if self.have_mosi else None
190 so_bits = self.misobits if self.have_miso else None
8a3c8792 191
12549f11
UH
192 # Pass MOSI and MISO to the next PD up the stack.
193 self.putpw(['DATA', si, so])
cddd11bc 194 self.putpw(['BITS', si_bits, so_bits])
b1bb5eed 195
12549f11
UH
196 # Annotations.
197 if self.have_miso:
198 self.putw([0, ['%02X' % self.misodata]])
199 if self.have_mosi:
200 self.putw([1, ['%02X' % self.mosidata]])
201
202 # Meta bitrate.
8a3c8792
BV
203 elapsed = 1 / float(self.samplerate) * (self.samplenum - self.startsample + 1)
204 bitrate = int(1 / elapsed * self.options['wordsize'])
205 self.put(self.startsample, self.samplenum, self.out_bitrate, bitrate)
206
12549f11 207 if self.have_cs and self.cs_was_deasserted_during_data_word:
cddd11bc 208 self.putw([4, ['CS# was deasserted during this data word!']])
191ec8c5
UH
209
210 # Reset decoder state.
12549f11
UH
211 self.misodata = 0 if self.have_miso else None
212 self.mosidata = 0 if self.have_mosi else None
cddd11bc
UH
213 self.misobits = [] if self.have_miso else None
214 self.mosibits = [] if self.have_mosi else None
12549f11 215 self.bitcount = 0
191ec8c5 216
bcd14870 217 def find_clk_edge(self, miso, mosi, clk, cs):
efa64173 218 if self.have_cs and self.oldcs != cs:
191ec8c5 219 # Send all CS# pin value changes.
c515eed7 220 self.put(self.samplenum, self.samplenum, self.out_python,
191ec8c5
UH
221 ['CS-CHANGE', self.oldcs, cs])
222 self.oldcs = cs
efa64173 223 # Reset decoder state when CS# changes (and the CS# pin is used).
12549f11
UH
224 self.misodata = 0 if self.have_miso else None
225 self.mosidata = 0 if self.have_mosi else None
226 self.bitcount = 0
191ec8c5
UH
227
228 # Ignore sample if the clock pin hasn't changed.
bcd14870 229 if clk == self.oldclk:
191ec8c5 230 return
b1bb5eed 231
bcd14870 232 self.oldclk = clk
b1bb5eed 233
191ec8c5
UH
234 # Sample data on rising/falling clock edge (depends on mode).
235 mode = spi_mode[self.options['cpol'], self.options['cpha']]
bcd14870 236 if mode == 0 and clk == 0: # Sample on rising clock edge
191ec8c5 237 return
bcd14870 238 elif mode == 1 and clk == 1: # Sample on falling clock edge
191ec8c5 239 return
bcd14870 240 elif mode == 2 and clk == 1: # Sample on falling clock edge
191ec8c5 241 return
bcd14870 242 elif mode == 3 and clk == 0: # Sample on rising clock edge
191ec8c5
UH
243 return
244
245 # Found the correct clock edge, now get the SPI bit(s).
bcd14870 246 self.handle_bit(miso, mosi, clk, cs)
191ec8c5
UH
247
248 def decode(self, ss, es, data):
8a3c8792
BV
249 if self.samplerate is None:
250 raise Exception("Cannot decode without samplerate.")
12549f11 251 # Either MISO or MOSI can be omitted (but not both). CS# is optional.
191ec8c5 252 for (self.samplenum, pins) in data:
01329e88 253
191ec8c5
UH
254 # Ignore identical samples early on (for performance reasons).
255 if self.oldpins == pins:
256 continue
bcd14870 257 self.oldpins, (clk, miso, mosi, cs) = pins, pins
12549f11
UH
258 self.have_miso = (miso in (0, 1))
259 self.have_mosi = (mosi in (0, 1))
efa64173 260 self.have_cs = (cs in (0, 1))
b1bb5eed 261
191ec8c5
UH
262 # State machine.
263 if self.state == 'IDLE':
bcd14870 264 self.find_clk_edge(miso, mosi, clk, cs)
191ec8c5
UH
265 else:
266 raise Exception('Invalid state: %s' % self.state)
ad2dc0de 267