]> sigrok.org Git - libsigrokdecode.git/blame - decoders/st25r39xx_spi/pd.py
st25r39xx_spi: Add st25r39xx PD using spi
[libsigrokdecode.git] / decoders / st25r39xx_spi / pd.py
CommitLineData
a43eba6a 1##
2## This file is part of the libsigrokdecode project.
3##
4## Copyright (C) 2019-2020 Benjamin Vernoux <bvernoux@gmail.com>
5##
6## This program is free software; you can redistribute it and/or modify
7## it under the terms of the GNU General Public License as published by
8## the Free Software Foundation; either version 2 of the License, or
9## (at your option) any later version.
10##
11## This program is distributed in the hope that it will be useful,
12## but WITHOUT ANY WARRANTY; without even the implied warranty of
13## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14## GNU General Public License for more details.
15##
16## You should have received a copy of the GNU General Public License
17## along with this program; if not, see <http://www.gnu.org/licenses/>.
18##
19## v0.1 - 17 September 2019 B.VERNOUX using ST25R3916 Datasheet DS12484 Rev 1 (January 2019)
20## v0.2 - 28 April 2020 B.VERNOUX using ST25R3916 Datasheet DS12484 Rev 2 (December 2019) https://www.st.com/resource/en/datasheet/st25r3916.pdf
21## v0.3 - 17 June 2020 B.VERNOUX using ST25R3916 Datasheet DS12484 Rev 3 (04 June 2020) https://www.st.com/resource/en/datasheet/st25r3916.pdf
22
23import sigrokdecode as srd
24from collections import namedtuple
25from common.srdhelper import SrdIntEnum
26from .lists import *
27
28Ann = SrdIntEnum.from_str('Ann', 'BURST_READ BURST_WRITE \
29 BURST_READB BURST_WRITEB BURST_READT BURST_WRITET \
30 DIRECTCMD FIFO_WRITE FIFO_READ STATUS WARN')
31
32Pos = namedtuple('Pos', ['ss', 'es'])
33Data = namedtuple('Data', ['mosi', 'miso'])
34
35class Decoder(srd.Decoder):
36 api_version = 3
37 id = 'st25r39xx_spi'
38 name = 'ST25R39xx'
39 longname = 'STMicroelectronics ST25R39xx'
40 desc = 'High performance NFC universal device and EMVCo reader.'
41 license = 'gplv2+'
42 inputs = ['spi']
43 outputs = []
44 tags = ['IC', 'Wireless/RF', 'NFC']
45 annotations = (
46 ('Read', 'Burst register read'),
47 ('Write', 'Burst register write'),
48 ('ReadB', 'Burst register SpaceB read'),
49 ('WriteB', 'Burst register SpaceB write'),
50 ('ReadT', 'Burst register Test read'),
51 ('WriteT', 'Burst register Test write'),
52 ('Cmd', 'Direct command'),
53 ('FIFOW', 'FIFO write'),
54 ('FIFOR', 'FIFO read'),
55 ('status_reg', 'Status register'),
56 ('warning', 'Warning'),
57 )
58 annotation_rows = (
59 ('reg', 'Regs', (Ann.prefixes('BURST_'))),
60 ('cmds', 'Commands', (Ann.DIRECTCMD,)),
61 ('data', 'Data', (Ann.prefixes('FIFO_'))),
62 ('status', 'Status register', (Ann.STATUS,)),
63 ('warnings', 'Warnings', (Ann.WARN,)),
64 )
65
66 def __init__(self):
67 self.reset()
68
69 def reset(self):
70 self.next()
71 self.requirements_met = True
72 self.cs_was_released = False
73
74 def start(self):
75 self.out_ann = self.register(srd.OUTPUT_ANN)
76
77 def warn(self, pos, msg):
78 '''Put a warning message 'msg' at 'pos'.'''
79 self.put(pos.ss, pos.es, self.out_ann, [Ann.WARN, [msg]])
80
81 def putp(self, pos, ann, msg):
82 '''Put an annotation message 'msg' at 'pos'.'''
83 self.put(pos.ss, pos.es, self.out_ann, [ann, [msg]])
84
85 def putp2(self, pos, ann, msg1, msg2):
86 '''Put an annotation message 'msg' at 'pos'.'''
87 self.put(pos.ss, pos.es, self.out_ann, [ann, [msg1, msg2]])
88
89 def next(self):
90 '''Resets the decoder after a complete command was decoded.'''
91 # 'True' for the first byte after CS# went low.
92 self.first = True
93
94 # The current command, and the minimum and maximum number
95 # of data bytes to follow.
96 self.cmd = None
97 self.min = 0
98 self.max = 0
99
100 # Used to collect the bytes after the command byte
101 # (and the start/end sample number).
102 self.mb = []
103 self.ss_mb = -1
104 self.es_mb = -1
105
106 def mosi_bytes(self):
107 '''Returns the collected MOSI bytes of a multi byte command.'''
108 return [b.mosi for b in self.mb]
109
110 def miso_bytes(self):
111 '''Returns the collected MISO bytes of a multi byte command.'''
112 return [b.miso for b in self.mb]
113
114 def decode_command(self, pos, b):
115 '''Decodes the command byte 'b' at position 'pos' and prepares
116 the decoding of the following data bytes.'''
117 c = self.parse_command(b)
118 if c is None:
119 self.warn(pos, 'unknown command')
120 return
121
122 self.cmd, self.dat, self.min, self.max = c
123
124 if self.cmd == 'Cmd':
125 self.putp(pos, Ann.DIRECTCMD, self.format_command())
126 else:
127 # Don't output anything now, the command is merged with
128 # the data bytes following it.
129 self.ss_mb = pos.ss
130
131 def format_command(self):
132 '''Returns the label for the current command.'''
133 if self.cmd in ('Write', 'Read', 'WriteB', 'ReadB', 'WriteT', 'ReadT', 'FIFO Write', 'FIFO Read'):
134 return self.cmd
135 if self.cmd == 'Cmd':
136 reg = dir_cmd.get(self.dat, 'unknown direct command')
137 return '{} {}'.format(self.cmd, reg)
138 else:
139 return 'TODO Cmd {}'.format(self.cmd)
140
141 def parse_command(self, b):
142 '''Parses the command byte.
143 Returns a tuple consisting of:
144 - the name of the command
145 - additional data needed to dissect the following bytes
146 - minimum number of following bytes
147 - maximum number of following bytes (None for infinite)
148 '''
149 addr = b & 0x3F
150 # previous command was 'Space B'
151 if self.cmd == 'Space B':
152 if (b & 0xC0) == 0x00:
153 return ('WriteB', addr, 1, 99999)
154 if (b & 0xC0) == 0x40:
155 return ('ReadB', addr, 1, 99999)
156 else:
157 self.warn(pos, 'unknown address/command combination')
158 # previous command was 'TestAccess'
159 elif self.cmd == 'TestAccess':
160 if (b & 0xC0) == 0x00:
161 return ('WriteT', addr, 1, 99999)
162 if (b & 0xC0) == 0x40:
163 return ('ReadT', addr, 1, 99999)
164 else:
165 self.warn(pos, 'unknown address/command combination')
166 else:
167 # Space A regs or other operation modes (except Space B)
168 # Register Write 0b00xxxxxx 0x00 to 0x3F => 'Write'
169 # Register Read 0b01xxxxxx 0x40 to 0x7F => 'Read'
170 if (b <= 0x7F):
171 if (b & 0xC0) == 0x00:
172 return ('Write', addr, 1, 99999)
173 if (b & 0xC0) == 0x40:
174 return ('Read', addr, 1, 99999)
175 else:
176 self.warn(pos, 'unknown address/command combination')
177 else:
178 # FIFO Load 0b10000000 0x80 => 'FIFO Write'
179 # PT_memory loadA-config 0b10100000 0xA0 => 'Write'
180 # PT_memory loadF-config 0b10101000 0xA8 => 'Write'
181 # PT_memory loadTSN data 0b10101100 0xAC => 'Write'
182 # PT_memory Read 0b10111111 0xBF => 'Read'
183 # FIFO Read 0b10011111 0x9F => 'FIFO Read'
184 # Direct Command 0b11xxx1xx 0xC0 to 0xE8 => 'Cmd'
185 # Register Space-B Access 0b11111011 0xFB => 'Space B'
186 # Register Test Access 0b11111100 0xFC => 'TestAccess'
187 if b == 0x80:
188 return ('FIFO Write', b, 1, 99999)
189 if b == 0xA0:
190 return ('Write', b, 1, 99999)
191 if b == 0xA8:
192 return ('Write', b, 1, 99999)
193 if b == 0xAC:
194 return ('Write', b, 1, 99999)
195 if b == 0xBF:
196 return ('Read', b, 1, 99999)
197 if b == 0x9F:
198 return ('FIFO Read', b, 1, 99999)
199 if (b >= 0x0C and b <= 0xE8) :
200 return ('Cmd', b, 0, 0)
201 if b == 0xFB:
202 return ('Space B', b, 0, 0)
203 if b == 0xFC:
204 return ('TestAccess', b, 0, 0)
205 else:
206 self.warn(pos, 'unknown address/command combination')
207
208 def decode_reg(self, pos, ann, regid, data):
209 '''Decodes a register.
210 pos -- start and end sample numbers of the register
211 ann -- the annotation number that is used to output the register.
212 regid -- may be either an integer used as a key for the 'regs'
213 dictionary, or a string directly containing a register name.'
214 data -- the register content.
215 '''
216 if type(regid) == int:
217 if (ann == Ann.FIFO_READ) or (ann == Ann.FIFO_WRITE):
218 name = ''
219 elif (ann == Ann.BURST_READB) or (ann == Ann.BURST_WRITEB):
220 # Get the name of the register.
221 if regid not in regsSpaceB:
222 self.warn(pos, 'unknown register SpaceB')
223 return
224 name = '{} ({:02X})'.format(regsSpaceB[regid], regid)
225 elif (ann == Ann.BURST_READT) or (ann == Ann.BURST_WRITET):
226 # Get the name of the register.
227 if regid not in regsTest:
228 self.warn(pos, 'unknown register Test')
229 return
230 name = '{} ({:02X})'.format(regsTest[regid], regid)
231 else:
232 # Get the name of the register.
233 if regid not in regsSpaceA:
234 self.warn(pos, 'unknown register SpaceA')
235 return
236 name = '{} ({:02X})'.format(regsSpaceA[regid], regid)
237 else:
238 name = regid
239
240 if regid == 'STATUS' and ann == Ann.STATUS:
241 label = 'Status'
242 self.decode_status_reg(pos, ann, data, label)
243 else:
244 label = '{}: {}'.format(self.format_command(), name)
245 self.decode_mb_data(pos, ann, data, label)
246
247 def decode_status_reg(self, pos, ann, data, label):
248 '''Decodes the data bytes 'data' of a status register at position
249 'pos'. The decoded data is prefixed with 'label'.'''
250
251 def decode_mb_data(self, pos, ann, data, label):
252 '''Decodes the data bytes 'data' of a multibyte command at position
253 'pos'. The decoded data is prefixed with 'label'.'''
254
255 def escape(b):
256 return '{:02X}'.format(b)
257
258 data = ' '.join([escape(b) for b in data])
259 if (ann == Ann.FIFO_WRITE) or (ann == Ann.FIFO_READ):
260 text = '{}{}'.format(label, data)
261 else:
262 text = '{} = {}'.format(label, data)
263 self.putp(pos, ann, text)
264
265 def finish_command(self, pos):
266 '''Decodes the remaining data bytes at position 'pos'.'''
267 if self.cmd == 'Write':
268 self.decode_reg(pos, Ann.BURST_WRITE, self.dat, self.mosi_bytes())
269 elif self.cmd == 'Read':
270 self.decode_reg(pos, Ann.BURST_READ, self.dat, self.miso_bytes())
271 elif self.cmd == 'WriteB':
272 self.decode_reg(pos, Ann.BURST_WRITEB, self.dat, self.mosi_bytes())
273 elif self.cmd == 'ReadB':
274 self.decode_reg(pos, Ann.BURST_READB, self.dat, self.miso_bytes())
275 elif self.cmd == 'WriteT':
276 self.decode_reg(pos, Ann.BURST_WRITET, self.dat, self.mosi_bytes())
277 elif self.cmd == 'ReadT':
278 self.decode_reg(pos, Ann.BURST_READT, self.dat, self.miso_bytes())
279 elif self.cmd == 'FIFO Write':
280 self.decode_reg(pos, Ann.FIFO_WRITE, self.dat, self.mosi_bytes())
281 elif self.cmd == 'FIFO Read':
282 self.decode_reg(pos, Ann.FIFO_READ, self.dat, self.miso_bytes())
283 elif self.cmd == 'Cmd':
284 self.decode_reg(pos, Ann.DIRECTCMD, self.dat, self.mosi_bytes())
285 else:
286 self.warn(pos, 'unhandled command')
287
288 def decode(self, ss, es, data):
289 if not self.requirements_met:
290 return
291
292 ptype, data1, data2 = data
293
294 if ptype == 'CS-CHANGE':
295 if data1 is None:
296 if data2 is None:
297 self.requirements_met = False
298 raise ChannelError('CS# pin required.')
299 elif data2 == 1:
300 self.cs_was_released = True
301
302 if data1 == 0 and data2 == 1:
303 # Rising edge, the complete command is transmitted, process
304 # the bytes that were sent after the command byte.
305 if self.cmd:
306 # Check if we got the minimum number of data bytes
307 # after the command byte.
308 if len(self.mb) < self.min:
309 self.warn((ss, ss), 'missing data bytes')
310 elif self.mb:
311 self.finish_command(Pos(self.ss_mb, self.es_mb))
312
313 self.next()
314 self.cs_was_released = True
315
316 elif ptype == 'DATA' and self.cs_was_released:
317 mosi, miso = data1, data2
318 pos = Pos(ss, es)
319
320 if miso is None or mosi is None:
321 self.requirements_met = False
322 raise ChannelError('Both MISO and MOSI pins are required.')
323
324 if self.first:
325 # Register Space-B Access 0b11111011 0xFB => 'Space B'
326 if mosi == 0xFB:
327 self.first = True
328 # First MOSI byte 'Space B' command.
329 self.decode_command(pos, mosi)
330 # First MISO byte is always the status register.
331 #self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
332 # Register TestAccess Access 0b11111100 0xFC => 'TestAccess'
333 elif mosi == 0xFC:
334 self.first = True
335 # First MOSI byte 'TestAccess' command.
336 self.decode_command(pos, mosi)
337 # First MISO byte is always the status register.
338 #self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
339 else:
340 self.first = False
341 # First MOSI byte is always the command.
342 self.decode_command(pos, mosi)
343 # First MISO byte is always the status register.
344 #self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
345 else:
346 if not self.cmd or len(self.mb) >= self.max:
347 self.warn(pos, 'excess byte')
348 else:
349 # Collect the bytes after the command byte.
350 if self.ss_mb == -1:
351 self.ss_mb = ss
352 self.es_mb = es
353 self.mb.append(Data(mosi, miso))