]> sigrok.org Git - libsigrokdecode.git/blob - decoders/swd/pd.py
swd: Add SWD protocol decoder for ARM Serial Wire Debug format.
[libsigrokdecode.git] / decoders / swd / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2014 Angus Gratton <gus@projectgus.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, write to the Free Software
18 ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19 ##
20
21 import sigrokdecode as srd
22 import re, traceback
23
24 '''
25 OUTPUT_PYTHON format:
26
27 Packet:
28 [<ptype>, <pdata>]
29
30 <ptype>:
31  - 'AP_READ' (AP read)
32  - 'DP_READ' (DP read)
33  - 'AP_WRITE' (AP write)
34  - 'DP_WRITE' (DP write)
35  - 'LINE_RESET' (line reset sequence)
36
37 <pdata>:
38   - tuple of address, ack state, data for the given sequence
39 '''
40
41 swd_states = [
42     'IDLE', # Idle/unknown
43     'REQUEST', # Request phase (first 8 bits)
44     'ACK', # Ack phase (next 3 bits)
45     'READ', # Reading phase (next 32 bits for reads)
46     'WRITE', # Writing phase (next 32 bits for write)
47     'DPARITY', # Data parity phase
48 ]
49
50 # Regexes for matching SWD data out of bitstring ('1' / '0' characters) format
51 RE_SWDSWITCH = re.compile(bin(0xE79E)[:1:-1] + '$')
52 RE_SWDREQ = re.compile(r'1(?P<apdp>.)(?P<rw>.)(?P<addr>..)(?P<parity>.)01$')
53 RE_IDLE = re.compile('0' * 50 + '$')
54
55 # Sample edges
56 RISING = 1
57 FALLING = 0
58
59 ADDR_DP_SELECT = 0x8
60 ADDR_DP_CTRLSTAT = 0x4
61
62 BIT_SELECT_CTRLSEL = 1
63 BIT_CTRLSTAT_ORUNDETECT = 1
64
65 ANNOTATIONS = ['reset', 'enable', 'read', 'write', 'ack', 'data', 'parity']
66
67 class Decoder(srd.Decoder):
68     api_version = 2
69     id = 'swd'
70     name = 'SWD'
71     longname = 'Serial Wire Debug'
72     desc = 'Two-wire protocol for debug access to ARM CPUs.'
73     license = 'gplv2+'
74     inputs = ['logic']
75     outputs = ['swd']
76     channels = (
77         {'id': 'swclk', 'name': 'SWCLK', 'desc': 'Master clock'},
78         {'id': 'swdio', 'name': 'SWDIO', 'desc': 'Data input/output'},
79     )
80     options = (
81         {'id': 'strict_start',
82          'desc': 'Wait for a line reset before starting to decode',
83          'default': 'no', 'values': ('yes', 'no')},
84     )
85     annotations = (
86         ('reset', 'RESET'),
87         ('enable', 'ENABLE'),
88         ('read', 'READ'),
89         ('write', 'WRITE'),
90         ('ack', 'ACK'),
91         ('data', 'DATA'),
92         ('parity', 'PARITY'),
93     )
94
95     def __init__(self, **kwargs):
96         # SWD data/clock state
97         self.state = 'UNKNOWN'
98         self.oldclk = -1
99         self.sample_edge = RISING
100         self.ack = None # Ack state of the current phase
101         self.ss_req = 0 # Start sample of current req
102         self.turnaround = 0 # Number of turnaround edges to ignore before continuing
103         self.bits = '' # Bits from SWDIO are accumulated here, matched against expected sequences
104         self.samplenums = [] # Sample numbers that correspond to the samples in self.bits
105         self.linereset_count = 0
106
107         # SWD debug port state
108         self.data = None
109         self.addr = None
110         self.rw = None # Are we inside an SWD read or a write?
111         self.ctrlsel = 0 # 'ctrlsel' is bit 0 in the SELECT register.
112         self.orundetect = 0 # 'orundetect' is bit 0 in the CTRLSTAT register.
113
114     def start(self):
115         self.out_ann = self.register(srd.OUTPUT_ANN)
116         self.out_python = self.register(srd.OUTPUT_PYTHON)
117         if self.options['strict_start'] == 'no':
118             self.state = 'REQ' # No need to wait for a LINE RESET.
119
120     def putx(self, ann, length, data):
121         '''Output annotated data.'''
122         ann = ANNOTATIONS.index(ann)
123         try:
124             ss = self.samplenums[-length]
125         except IndexError:
126             ss = self.samplenums[0]
127         if self.state == 'REQ':
128             self.ss_req = ss
129         es = self.samplenum
130         self.put(ss, es, self.out_ann, [ann, [data]])
131
132     def putp(self, ptype, pdata):
133         self.put(self.ss_req, self.samplenum, self.out_python, [ptype, pdata])
134
135     def put_python_data(self):
136         '''Emit Python data item based on current SWD packet contents.'''
137         ptype = {
138             ('AP', 'R'): 'AP_READ',
139             ('AP', 'W'): 'AP_WRITE',
140             ('DP', 'R'): 'DP_READ',
141             ('DP', 'W'): 'DP_WRITE',
142         }[(self.apdp, self.rw)]
143         self.putp(ptype, (self.addr, self.data, self.ack))
144
145     def decode(self, ss, es, data):
146         try:
147             return self._decode(ss, es, data)
148         except:
149             traceback.print_exc()
150             raise
151
152     def _decode(self, ss, es, data):
153         for (self.samplenum, (clk, dio)) in data:
154             if clk == self.oldclk:
155                 continue # Not a clock edge.
156             self.oldclk = clk
157
158             # Count rising edges with DIO held high,
159             # as a line reset (50+ high edges) can happen from any state.
160             if clk == RISING:
161                 if dio == 1:
162                     self.linereset_count += 1
163                 else:
164                     if self.linereset_count >= 50:
165                         self.putx('reset', self.linereset_count, 'LINERESET')
166                         self.putp('LINE_RESET', None)
167                         self.reset_state()
168                     self.linereset_count = 0
169
170             # Otherwise, we only care about either rising or falling edges
171             # (depending on sample_edge, set according to current state).
172             if clk != self.sample_edge:
173                 continue
174
175             # Turnaround bits get skipped.
176             if self.turnaround > 0:
177                 self.turnaround -= 1
178                 continue
179
180             self.bits += str(dio)
181             self.samplenums.append(self.samplenum)
182             {
183                 'UNKNOWN': self.handle_unknown_edge,
184                 'REQ': self.handle_req_edge,
185                 'ACK': self.handle_ack_edge,
186                 'DATA': self.handle_data_edge,
187                 'DPARITY': self.handle_dparity_edge,
188             }[self.state]()
189
190     def next_state(self):
191         '''Step to the next SWD state, reset internal counters accordingly.'''
192         self.bits = ''
193         self.samplenums = []
194         self.linereset_count = 0
195         if self.state == 'UNKNOWN':
196             self.state = 'REQ'
197             self.sample_edge = RISING
198             self.turnaround = 0
199         elif self.state == 'REQ':
200             self.state = 'ACK'
201             self.sample_edge = FALLING
202             self.turnaround = 1
203         elif self.state == 'ACK':
204             self.state = 'DATA'
205             self.sample_edge = RISING if self.rw == 'W' else FALLING
206             self.turnaround = 0 if self.rw == 'R' else 2
207         elif self.state == 'DATA':
208             self.state = 'DPARITY'
209         elif self.state == 'DPARITY':
210             self.put_python_data()
211             self.state = 'REQ'
212             self.sample_edge = RISING
213             self.turnaround = 1 if self.rw == 'R' else 0
214
215     def reset_state(self):
216         '''Line reset (or equivalent), wait for a new pending SWD request.'''
217         if self.state != 'REQ': # Emit a Python data item.
218             self.put_python_data()
219         # Clear state.
220         self.bits = ''
221         self.samplenums = []
222         self.linereset_count = 0
223         self.turnaround = 0
224         self.sample_edge = RISING
225         self.data = ''
226         self.ack = None
227         self.state = 'REQ'
228
229     def handle_unknown_edge(self):
230         '''
231         Clock edge in the UNKNOWN state.
232         In the unknown state, clock edges get ignored until we see a line
233         reset (which is detected in the decode method, not here.)
234         '''
235         pass
236
237     def handle_req_edge(self):
238         '''Clock edge in the REQ state (waiting for SWD r/w request).'''
239         # Check for a JTAG->SWD enable sequence.
240         m = re.search(RE_SWDSWITCH, self.bits)
241         if m is not None:
242             self.putx('enable', 16, 'JTAG->SWD')
243             self.reset_state()
244             return
245
246         # Or a valid SWD Request packet.
247         m = re.search(RE_SWDREQ, self.bits)
248         if m is not None:
249             calc_parity = sum([int(x) for x in m.group('rw') + m.group('apdp') + m.group('addr')]) % 2
250             parity = '' if str(calc_parity) == m.group('parity') else 'E'
251             self.rw = 'R' if m.group('rw') == '1' else 'W'
252             self.apdp = 'AP' if m.group('apdp') == '1' else 'DP'
253             self.addr = int(m.group('addr')[::-1], 2) << 2
254             self.putx('read' if self.rw == 'R' else 'write', 8, self.get_address_description())
255             self.next_state()
256             return
257
258     def handle_ack_edge(self):
259         '''Clock edge in the ACK state (waiting for complete ACK sequence).'''
260         if len(self.bits) < 3:
261             return
262         if self.bits == '100':
263             self.putx('ack', 3, 'OK')
264             self.ack = 'OK'
265             self.next_state()
266         elif self.bits == '001':
267             self.putx('ack', 3, 'FAULT')
268             self.ack = 'FAULT'
269             if self.orundetect == 1:
270                 self.next_state()
271             else:
272                 self.reset_state()
273             self.turnaround = 1
274         elif self.bits == '010':
275             self.putx('ack', 3, 'WAIT')
276             self.ack = 'WAIT'
277             if self.orundetect == 1:
278                 self.next_state()
279             else:
280                 self.reset_state()
281             self.turnaround = 1
282         elif self.bits == '111':
283             self.putx('ack', 3, 'NOREPLY')
284             self.ack = 'NOREPLY'
285             self.reset_state()
286         else:
287             self.putx('ack', 3, 'ERROR')
288             self.ack = 'ERROR'
289             self.reset_state()
290
291     def handle_data_edge(self):
292         '''Clock edge in the DATA state (waiting for 32 bits to clock past).'''
293         if len(self.bits) < 32:
294             return
295         self.data = 0
296         self.dparity = 0
297         for x in range(32):
298             if self.bits[x] == '1':
299                 self.data += (1 << x)
300                 self.dparity += 1
301         self.dparity = self.dparity % 2
302
303         self.putx('data', 32, '0x%08x' % self.data)
304         self.next_state()
305
306     def handle_dparity_edge(self):
307         '''Clock edge in the DPARITY state (clocking in parity bit).'''
308         if str(self.dparity) != self.bits:
309             self.putx('parity', 1, str(self.dparity) + self.bits) # PARITY ERROR
310         elif self.rw == 'W':
311             self.handle_completed_write()
312         self.next_state()
313
314     def handle_completed_write(self):
315         '''
316         Update internal state of the debug port based on a completed
317         write operation.
318         '''
319         if self.apdp != 'DP':
320             return
321         elif self.addr == ADDR_DP_SELECT:
322             self.ctrlsel = self.data & BIT_SELECT_CTRLSEL
323         elif self.addr == ADDR_DP_CTRLSTAT and self.ctrlsel == 0:
324             self.orundetect = self.data & BIT_CTRLSTAT_ORUNDETECT
325
326     def get_address_description(self):
327         '''
328         Return a human-readable description of the currently selected address,
329         for annotated results.
330         '''
331         if self.apdp == 'DP':
332             if self.rw == 'R':
333                 # Tables 2-4 & 2-5 in ADIv5.2 spec ARM document IHI 0031C
334                 return {
335                     0: 'IDCODE',
336                     0x4: 'R CTRL/STAT' if self.ctrlsel == 0 else 'R DLCR',
337                     0x8: 'RESEND',
338                     0xC: 'RDBUFF'
339                 }[self.addr]
340             elif self.rw == 'W':
341                 # Tables 2-4 & 2-5 in ADIv5.2 spec ARM document IHI 0031C
342                 return {
343                     0: 'W ABORT',
344                     0x4: 'W CTRL/STAT' if self.ctrlsel == 0 else 'W DLCR',
345                     0x8: 'W SELECT',
346                     0xC: 'W RESERVED'
347                 }[self.addr]
348         elif self.apdp == 'AP':
349             if self.rw == 'R':
350                 return 'W AP%x' % self.addr
351             elif self.rw == 'W':
352                 return 'W AP%x' % self.addr
353
354         # Any legitimate operations shouldn't fall through to here, probably
355         # a decoder bug.
356         return '? %s%s%x' % (self.rw, self.apdp, self.addr)