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