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