2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2019-2020 Benjamin Vernoux <bvernoux@gmail.com>
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.
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.
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/>.
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
23 import sigrokdecode as srd
24 from collections import namedtuple
25 from common.srdhelper import SrdIntEnum
28 Ann = 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')
32 Pos = namedtuple('Pos', ['ss', 'es'])
33 Data = namedtuple('Data', ['mosi', 'miso'])
35 class Decoder(srd.Decoder):
39 longname = 'STMicroelectronics ST25R39xx'
40 desc = 'High performance NFC universal device and EMVCo reader.'
44 tags = ['IC', 'Wireless/RF', 'NFC']
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'),
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,)),
71 self.requirements_met = True
72 self.cs_was_released = False
75 self.out_ann = self.register(srd.OUTPUT_ANN)
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]])
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]])
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]])
90 '''Resets the decoder after a complete command was decoded.'''
91 # 'True' for the first byte after CS# went low.
94 # The current command, and the minimum and maximum number
95 # of data bytes to follow.
100 # Used to collect the bytes after the command byte
101 # (and the start/end sample number).
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]
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]
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)
119 self.warn(pos, 'unknown command')
122 self.cmd, self.dat, self.min, self.max = c
124 if self.cmd == 'Cmd':
125 self.putp(pos, Ann.DIRECTCMD, self.format_command())
127 # Don't output anything now, the command is merged with
128 # the data bytes following it.
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'):
135 if self.cmd == 'Cmd':
136 reg = dir_cmd.get(self.dat, 'unknown direct command')
137 return '{} {}'.format(self.cmd, reg)
139 return 'TODO Cmd {}'.format(self.cmd)
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)
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)
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)
165 self.warn(pos, 'unknown address/command combination')
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'
171 if (b & 0xC0) == 0x00:
172 return ('Write', addr, 1, 99999)
173 if (b & 0xC0) == 0x40:
174 return ('Read', addr, 1, 99999)
176 self.warn(pos, 'unknown address/command combination')
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'
188 return ('FIFO Write', b, 1, 99999)
190 return ('Write', b, 1, 99999)
192 return ('Write', b, 1, 99999)
194 return ('Write', b, 1, 99999)
196 return ('Read', b, 1, 99999)
198 return ('FIFO Read', b, 1, 99999)
199 if (b >= 0x0C and b <= 0xE8) :
200 return ('Cmd', b, 0, 0)
202 return ('Space B', b, 0, 0)
204 return ('TestAccess', b, 0, 0)
206 self.warn(pos, 'unknown address/command combination')
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.
216 if type(regid) == int:
217 if (ann == Ann.FIFO_READ) or (ann == Ann.FIFO_WRITE):
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')
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')
230 name = '{} ({:02X})'.format(regsTest[regid], regid)
232 # Get the name of the register.
233 if regid not in regsSpaceA:
234 self.warn(pos, 'unknown register SpaceA')
236 name = '{} ({:02X})'.format(regsSpaceA[regid], regid)
240 if regid == 'STATUS' and ann == Ann.STATUS:
242 self.decode_status_reg(pos, ann, data, label)
244 label = '{}: {}'.format(self.format_command(), name)
245 self.decode_mb_data(pos, ann, data, label)
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'.'''
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'.'''
256 return '{:02X}'.format(b)
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)
262 text = '{} = {}'.format(label, data)
263 self.putp(pos, ann, text)
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())
286 self.warn(pos, 'unhandled command')
288 def decode(self, ss, es, data):
289 if not self.requirements_met:
292 ptype, data1, data2 = data
294 if ptype == 'CS-CHANGE':
297 self.requirements_met = False
298 raise ChannelError('CS# pin required.')
300 self.cs_was_released = True
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.
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')
311 self.finish_command(Pos(self.ss_mb, self.es_mb))
314 self.cs_was_released = True
316 elif ptype == 'DATA' and self.cs_was_released:
317 mosi, miso = data1, data2
320 if miso is None or mosi is None:
321 self.requirements_met = False
322 raise ChannelError('Both MISO and MOSI pins are required.')
325 # Register Space-B Access 0b11111011 0xFB => 'Space B'
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'
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])
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])
346 if not self.cmd or len(self.mb) >= self.max:
347 self.warn(pos, 'excess byte')
349 # Collect the bytes after the command byte.
353 self.mb.append(Data(mosi, miso))