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/>.
20 import sigrokdecode as srd
21 from collections import namedtuple
22 from common.srdhelper import SrdIntEnum
25 Ann = SrdIntEnum.from_str('Ann', 'BURST_READ BURST_WRITE \
26 BURST_READB BURST_WRITEB BURST_READT BURST_WRITET \
27 DIRECTCMD FIFO_WRITE FIFO_READ STATUS WARN')
29 Pos = namedtuple('Pos', ['ss', 'es'])
30 Data = namedtuple('Data', ['mosi', 'miso'])
32 class Decoder(srd.Decoder):
35 name = 'ST25R39xx (SPI mode)'
36 longname = 'STMicroelectronics ST25R39xx'
37 desc = 'High performance NFC universal device and EMVCo reader protocol.'
41 tags = ['IC', 'Wireless/RF']
43 ('Read', 'Burst register read'),
44 ('Write', 'Burst register write'),
45 ('ReadB', 'Burst register SpaceB read'),
46 ('WriteB', 'Burst register SpaceB write'),
47 ('ReadT', 'Burst register Test read'),
48 ('WriteT', 'Burst register Test write'),
49 ('Cmd', 'Direct command'),
50 ('FIFOW', 'FIFO write'),
51 ('FIFOR', 'FIFO read'),
52 ('status_reg', 'Status register'),
53 ('warning', 'Warning'),
56 ('regs', 'Regs', (Ann.prefixes('BURST_'))),
57 ('cmds', 'Commands', (Ann.DIRECTCMD,)),
58 ('data', 'Data', (Ann.prefixes('FIFO_'))),
59 ('status', 'Status register', (Ann.STATUS,)),
60 ('warnings', 'Warnings', (Ann.WARN,)),
68 self.requirements_met = True
69 self.cs_was_released = False
72 self.out_ann = self.register(srd.OUTPUT_ANN)
74 def warn(self, pos, msg):
75 '''Put a warning message 'msg' at 'pos'.'''
76 self.put(pos.ss, pos.es, self.out_ann, [Ann.WARN, [msg]])
78 def putp(self, pos, ann, msg):
79 '''Put an annotation message 'msg' at 'pos'.'''
80 self.put(pos.ss, pos.es, self.out_ann, [ann, [msg]])
82 def putp2(self, pos, ann, msg1, msg2):
83 '''Put an annotation message 'msg' at 'pos'.'''
84 self.put(pos.ss, pos.es, self.out_ann, [ann, [msg1, msg2]])
87 '''Resets the decoder after a complete command was decoded.'''
88 # 'True' for the first byte after CS# went low.
91 # The current command, and the minimum and maximum number
92 # of data bytes to follow.
97 # Used to collect the bytes after the command byte
98 # (and the start/end sample number).
103 def mosi_bytes(self):
104 '''Returns the collected MOSI bytes of a multi byte command.'''
105 return [b.mosi for b in self.mb]
107 def miso_bytes(self):
108 '''Returns the collected MISO bytes of a multi byte command.'''
109 return [b.miso for b in self.mb]
111 def decode_command(self, pos, b):
112 '''Decodes the command byte 'b' at position 'pos' and prepares
113 the decoding of the following data bytes.'''
114 c = self.parse_command(b)
116 self.warn(pos, 'Unknown command')
119 self.cmd, self.dat, self.min, self.max = c
121 if self.cmd == 'Cmd':
122 self.putp(pos, Ann.DIRECTCMD, self.format_command())
124 # Don't output anything now, the command is merged with
125 # the data bytes following it.
128 def format_command(self):
129 '''Returns the label for the current command.'''
130 if self.cmd in ('Write', 'Read', 'WriteB', 'ReadB', 'WriteT', 'ReadT', 'FIFO Write', 'FIFO Read'):
132 if self.cmd == 'Cmd':
133 reg = dir_cmd.get(self.dat, 'Unknown direct command')
134 return '{} {}'.format(self.cmd, reg)
136 return 'TODO Cmd {}'.format(self.cmd)
138 def parse_command(self, b):
139 '''Parses the command byte.
140 Returns a tuple consisting of:
141 - the name of the command
142 - additional data needed to dissect the following bytes
143 - minimum number of following bytes
144 - maximum number of following bytes (None for infinite)
147 # previous command was 'Space B'
148 if self.cmd == 'Space B':
149 if (b & 0xC0) == 0x00:
150 return ('WriteB', addr, 1, 99999)
151 if (b & 0xC0) == 0x40:
152 return ('ReadB', addr, 1, 99999)
154 self.warn(pos, 'Unknown address/command combination')
155 # previous command was 'TestAccess'
156 elif self.cmd == 'TestAccess':
157 if (b & 0xC0) == 0x00:
158 return ('WriteT', addr, 1, 99999)
159 if (b & 0xC0) == 0x40:
160 return ('ReadT', addr, 1, 99999)
162 self.warn(pos, 'Unknown address/command combination')
164 # Space A regs or other operation modes (except Space B)
165 # Register Write 0b00xxxxxx 0x00 to 0x3F => 'Write'
166 # Register Read 0b01xxxxxx 0x40 to 0x7F => 'Read'
168 if (b & 0xC0) == 0x00:
169 return ('Write', addr, 1, 99999)
170 if (b & 0xC0) == 0x40:
171 return ('Read', addr, 1, 99999)
173 self.warn(pos, 'Unknown address/command combination')
175 # FIFO Load 0b10000000 0x80 => 'FIFO Write'
176 # PT_memory loadA-config 0b10100000 0xA0 => 'Write'
177 # PT_memory loadF-config 0b10101000 0xA8 => 'Write'
178 # PT_memory loadTSN data 0b10101100 0xAC => 'Write'
179 # PT_memory Read 0b10111111 0xBF => 'Read'
180 # FIFO Read 0b10011111 0x9F => 'FIFO Read'
181 # Direct Command 0b11xxx1xx 0xC0 to 0xE8 => 'Cmd'
182 # Register Space-B Access 0b11111011 0xFB => 'Space B'
183 # Register Test Access 0b11111100 0xFC => 'TestAccess'
185 return ('FIFO Write', b, 1, 99999)
187 return ('Write', b, 1, 99999)
189 return ('Write', b, 1, 99999)
191 return ('Write', b, 1, 99999)
193 return ('Read', b, 1, 99999)
195 return ('FIFO Read', b, 1, 99999)
196 if (b >= 0x0C and b <= 0xE8) :
197 return ('Cmd', b, 0, 0)
199 return ('Space B', b, 0, 0)
201 return ('TestAccess', b, 0, 0)
203 self.warn(pos, 'Unknown address/command combination')
205 def decode_reg(self, pos, ann, regid, data):
206 '''Decodes a register.
207 pos -- start and end sample numbers of the register
208 ann -- the annotation number that is used to output the register.
209 regid -- may be either an integer used as a key for the 'regs'
210 dictionary, or a string directly containing a register name.'
211 data -- the register content.
213 if type(regid) == int:
214 if (ann == Ann.FIFO_READ) or (ann == Ann.FIFO_WRITE):
216 elif (ann == Ann.BURST_READB) or (ann == Ann.BURST_WRITEB):
217 # Get the name of the register.
218 if regid not in regsSpaceB:
219 self.warn(pos, 'Unknown register SpaceB')
221 name = '{} ({:02X})'.format(regsSpaceB[regid], regid)
222 elif (ann == Ann.BURST_READT) or (ann == Ann.BURST_WRITET):
223 # Get the name of the register.
224 if regid not in regsTest:
225 self.warn(pos, 'Unknown register Test')
227 name = '{} ({:02X})'.format(regsTest[regid], regid)
229 # Get the name of the register.
230 if regid not in regsSpaceA:
231 self.warn(pos, 'Unknown register SpaceA')
233 name = '{} ({:02X})'.format(regsSpaceA[regid], regid)
237 if regid == 'STATUS' and ann == Ann.STATUS:
239 self.decode_status_reg(pos, ann, data, label)
241 label = '{}: {}'.format(self.format_command(), name)
242 self.decode_mb_data(pos, ann, data, label)
244 def decode_status_reg(self, pos, ann, data, label):
245 '''Decodes the data bytes 'data' of a status register at position
246 'pos'. The decoded data is prefixed with 'label'.'''
248 def decode_mb_data(self, pos, ann, data, label):
249 '''Decodes the data bytes 'data' of a multibyte command at position
250 'pos'. The decoded data is prefixed with 'label'.'''
253 return '{:02X}'.format(b)
255 data = ' '.join([escape(b) for b in data])
256 if (ann == Ann.FIFO_WRITE) or (ann == Ann.FIFO_READ):
257 text = '{}{}'.format(label, data)
259 text = '{} = {}'.format(label, data)
260 self.putp(pos, ann, text)
262 def finish_command(self, pos):
263 '''Decodes the remaining data bytes at position 'pos'.'''
264 if self.cmd == 'Write':
265 self.decode_reg(pos, Ann.BURST_WRITE, self.dat, self.mosi_bytes())
266 elif self.cmd == 'Read':
267 self.decode_reg(pos, Ann.BURST_READ, self.dat, self.miso_bytes())
268 elif self.cmd == 'WriteB':
269 self.decode_reg(pos, Ann.BURST_WRITEB, self.dat, self.mosi_bytes())
270 elif self.cmd == 'ReadB':
271 self.decode_reg(pos, Ann.BURST_READB, self.dat, self.miso_bytes())
272 elif self.cmd == 'WriteT':
273 self.decode_reg(pos, Ann.BURST_WRITET, self.dat, self.mosi_bytes())
274 elif self.cmd == 'ReadT':
275 self.decode_reg(pos, Ann.BURST_READT, self.dat, self.miso_bytes())
276 elif self.cmd == 'FIFO Write':
277 self.decode_reg(pos, Ann.FIFO_WRITE, self.dat, self.mosi_bytes())
278 elif self.cmd == 'FIFO Read':
279 self.decode_reg(pos, Ann.FIFO_READ, self.dat, self.miso_bytes())
280 elif self.cmd == 'Cmd':
281 self.decode_reg(pos, Ann.DIRECTCMD, self.dat, self.mosi_bytes())
283 self.warn(pos, 'Unhandled command {}'.format(self.cmd))
285 def decode(self, ss, es, data):
286 if not self.requirements_met:
289 ptype, data1, data2 = data
291 if ptype == 'CS-CHANGE':
294 self.requirements_met = False
295 raise ChannelError('CS# pin required.')
297 self.cs_was_released = True
299 if data1 == 0 and data2 == 1:
300 # Rising edge, the complete command is transmitted, process
301 # the bytes that were sent after the command byte.
303 # Check if we got the minimum number of data bytes
304 # after the command byte.
305 if len(self.mb) < self.min:
306 self.warn((ss, ss), 'Missing data bytes')
308 self.finish_command(Pos(self.ss_mb, self.es_mb))
311 self.cs_was_released = True
313 elif ptype == 'DATA' and self.cs_was_released:
314 mosi, miso = data1, data2
317 if miso is None or mosi is None:
318 self.requirements_met = False
319 raise ChannelError('Both MISO and MOSI pins are required.')
322 # Register Space-B Access 0b11111011 0xFB => 'Space B'
325 # First MOSI byte 'Space B' command.
326 self.decode_command(pos, mosi)
327 # First MISO byte is always the status register.
328 #self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
329 # Register TestAccess Access 0b11111100 0xFC => 'TestAccess'
332 # First MOSI byte 'TestAccess' command.
333 self.decode_command(pos, mosi)
334 # First MISO byte is always the status register.
335 #self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
338 # First MOSI byte is always the command.
339 self.decode_command(pos, mosi)
340 # First MISO byte is always the status register.
341 #self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
343 if not self.cmd or len(self.mb) >= self.max:
344 self.warn(pos, 'Excess byte')
346 # Collect the bytes after the command byte.
350 self.mb.append(Data(mosi, miso))