]>
Commit | Line | Data |
---|---|---|
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 | self.reset() | |
96 | ||
97 | def reset(self): | |
98 | # SWD data/clock state | |
99 | self.state = 'UNKNOWN' | |
100 | self.sample_edge = RISING | |
101 | self.ack = None # Ack state of the current phase | |
102 | self.ss_req = 0 # Start sample of current req | |
103 | self.turnaround = 0 # Number of turnaround edges to ignore before continuing | |
104 | self.bits = '' # Bits from SWDIO are accumulated here, matched against expected sequences | |
105 | self.samplenums = [] # Sample numbers that correspond to the samples in self.bits | |
106 | self.linereset_count = 0 | |
107 | ||
108 | # SWD debug port state | |
109 | self.data = None | |
110 | self.addr = None | |
111 | self.rw = None # Are we inside an SWD read or a write? | |
112 | self.ctrlsel = 0 # 'ctrlsel' is bit 0 in the SELECT register. | |
113 | self.orundetect = 0 # 'orundetect' is bit 0 in the CTRLSTAT register. | |
114 | ||
115 | def start(self): | |
116 | self.out_ann = self.register(srd.OUTPUT_ANN) | |
117 | self.out_python = self.register(srd.OUTPUT_PYTHON) | |
118 | if self.options['strict_start'] == 'no': | |
119 | self.state = 'REQ' # No need to wait for a LINE RESET. | |
120 | ||
121 | def putx(self, ann, length, data): | |
122 | '''Output annotated data.''' | |
123 | ann = ANNOTATIONS.index(ann) | |
124 | try: | |
125 | ss = self.samplenums[-length] | |
126 | except IndexError: | |
127 | ss = self.samplenums[0] | |
128 | if self.state == 'REQ': | |
129 | self.ss_req = ss | |
130 | es = self.samplenum | |
131 | self.put(ss, es, self.out_ann, [ann, [data]]) | |
132 | ||
133 | def putp(self, ptype, pdata): | |
134 | self.put(self.ss_req, self.samplenum, self.out_python, [ptype, pdata]) | |
135 | ||
136 | def put_python_data(self): | |
137 | '''Emit Python data item based on current SWD packet contents.''' | |
138 | ptype = { | |
139 | ('AP', 'R'): 'AP_READ', | |
140 | ('AP', 'W'): 'AP_WRITE', | |
141 | ('DP', 'R'): 'DP_READ', | |
142 | ('DP', 'W'): 'DP_WRITE', | |
143 | }[(self.apdp, self.rw)] | |
144 | self.putp(ptype, (self.addr, self.data, self.ack)) | |
145 | ||
146 | def decode(self): | |
147 | while True: | |
148 | # Wait for any clock edge. | |
149 | clk, dio = self.wait({0: 'e'}) | |
150 | ||
151 | # Count rising edges with DIO held high, | |
152 | # as a line reset (50+ high edges) can happen from any state. | |
153 | if clk == RISING: | |
154 | if dio == 1: | |
155 | self.linereset_count += 1 | |
156 | else: | |
157 | if self.linereset_count >= 50: | |
158 | self.putx('reset', self.linereset_count, 'LINERESET') | |
159 | self.putp('LINE_RESET', None) | |
160 | self.reset_state() | |
161 | self.linereset_count = 0 | |
162 | ||
163 | # Otherwise, we only care about either rising or falling edges | |
164 | # (depending on sample_edge, set according to current state). | |
165 | if clk != self.sample_edge: | |
166 | continue | |
167 | ||
168 | # Turnaround bits get skipped. | |
169 | if self.turnaround > 0: | |
170 | self.turnaround -= 1 | |
171 | continue | |
172 | ||
173 | self.bits += str(dio) | |
174 | self.samplenums.append(self.samplenum) | |
175 | { | |
176 | 'UNKNOWN': self.handle_unknown_edge, | |
177 | 'REQ': self.handle_req_edge, | |
178 | 'ACK': self.handle_ack_edge, | |
179 | 'DATA': self.handle_data_edge, | |
180 | 'DPARITY': self.handle_dparity_edge, | |
181 | }[self.state]() | |
182 | ||
183 | def next_state(self): | |
184 | '''Step to the next SWD state, reset internal counters accordingly.''' | |
185 | self.bits = '' | |
186 | self.samplenums = [] | |
187 | self.linereset_count = 0 | |
188 | if self.state == 'UNKNOWN': | |
189 | self.state = 'REQ' | |
190 | self.sample_edge = RISING | |
191 | self.turnaround = 0 | |
192 | elif self.state == 'REQ': | |
193 | self.state = 'ACK' | |
194 | self.sample_edge = FALLING | |
195 | self.turnaround = 1 | |
196 | elif self.state == 'ACK': | |
197 | self.state = 'DATA' | |
198 | self.sample_edge = RISING if self.rw == 'W' else FALLING | |
199 | self.turnaround = 0 if self.rw == 'R' else 2 | |
200 | elif self.state == 'DATA': | |
201 | self.state = 'DPARITY' | |
202 | elif self.state == 'DPARITY': | |
203 | self.put_python_data() | |
204 | self.state = 'REQ' | |
205 | self.sample_edge = RISING | |
206 | self.turnaround = 1 if self.rw == 'R' else 0 | |
207 | ||
208 | def reset_state(self): | |
209 | '''Line reset (or equivalent), wait for a new pending SWD request.''' | |
210 | if self.state != 'REQ': # Emit a Python data item. | |
211 | self.put_python_data() | |
212 | # Clear state. | |
213 | self.bits = '' | |
214 | self.samplenums = [] | |
215 | self.linereset_count = 0 | |
216 | self.turnaround = 0 | |
217 | self.sample_edge = RISING | |
218 | self.data = '' | |
219 | self.ack = None | |
220 | self.state = 'REQ' | |
221 | ||
222 | def handle_unknown_edge(self): | |
223 | ''' | |
224 | Clock edge in the UNKNOWN state. | |
225 | In the unknown state, clock edges get ignored until we see a line | |
226 | reset (which is detected in the decode method, not here.) | |
227 | ''' | |
228 | pass | |
229 | ||
230 | def handle_req_edge(self): | |
231 | '''Clock edge in the REQ state (waiting for SWD r/w request).''' | |
232 | # Check for a JTAG->SWD enable sequence. | |
233 | m = re.search(RE_SWDSWITCH, self.bits) | |
234 | if m is not None: | |
235 | self.putx('enable', 16, 'JTAG->SWD') | |
236 | self.reset_state() | |
237 | return | |
238 | ||
239 | # Or a valid SWD Request packet. | |
240 | m = re.search(RE_SWDREQ, self.bits) | |
241 | if m is not None: | |
242 | calc_parity = sum([int(x) for x in m.group('rw') + m.group('apdp') + m.group('addr')]) % 2 | |
243 | parity = '' if str(calc_parity) == m.group('parity') else 'E' | |
244 | self.rw = 'R' if m.group('rw') == '1' else 'W' | |
245 | self.apdp = 'AP' if m.group('apdp') == '1' else 'DP' | |
246 | self.addr = int(m.group('addr')[::-1], 2) << 2 | |
247 | self.putx('read' if self.rw == 'R' else 'write', 8, self.get_address_description()) | |
248 | self.next_state() | |
249 | return | |
250 | ||
251 | def handle_ack_edge(self): | |
252 | '''Clock edge in the ACK state (waiting for complete ACK sequence).''' | |
253 | if len(self.bits) < 3: | |
254 | return | |
255 | if self.bits == '100': | |
256 | self.putx('ack', 3, 'OK') | |
257 | self.ack = 'OK' | |
258 | self.next_state() | |
259 | elif self.bits == '001': | |
260 | self.putx('ack', 3, 'FAULT') | |
261 | self.ack = 'FAULT' | |
262 | if self.orundetect == 1: | |
263 | self.next_state() | |
264 | else: | |
265 | self.reset_state() | |
266 | self.turnaround = 1 | |
267 | elif self.bits == '010': | |
268 | self.putx('ack', 3, 'WAIT') | |
269 | self.ack = 'WAIT' | |
270 | if self.orundetect == 1: | |
271 | self.next_state() | |
272 | else: | |
273 | self.reset_state() | |
274 | self.turnaround = 1 | |
275 | elif self.bits == '111': | |
276 | self.putx('ack', 3, 'NOREPLY') | |
277 | self.ack = 'NOREPLY' | |
278 | self.reset_state() | |
279 | else: | |
280 | self.putx('ack', 3, 'ERROR') | |
281 | self.ack = 'ERROR' | |
282 | self.reset_state() | |
283 | ||
284 | def handle_data_edge(self): | |
285 | '''Clock edge in the DATA state (waiting for 32 bits to clock past).''' | |
286 | if len(self.bits) < 32: | |
287 | return | |
288 | self.data = 0 | |
289 | self.dparity = 0 | |
290 | for x in range(32): | |
291 | if self.bits[x] == '1': | |
292 | self.data += (1 << x) | |
293 | self.dparity += 1 | |
294 | self.dparity = self.dparity % 2 | |
295 | ||
296 | self.putx('data', 32, '0x%08x' % self.data) | |
297 | self.next_state() | |
298 | ||
299 | def handle_dparity_edge(self): | |
300 | '''Clock edge in the DPARITY state (clocking in parity bit).''' | |
301 | if str(self.dparity) != self.bits: | |
302 | self.putx('parity', 1, str(self.dparity) + self.bits) # PARITY ERROR | |
303 | elif self.rw == 'W': | |
304 | self.handle_completed_write() | |
305 | self.next_state() | |
306 | ||
307 | def handle_completed_write(self): | |
308 | ''' | |
309 | Update internal state of the debug port based on a completed | |
310 | write operation. | |
311 | ''' | |
312 | if self.apdp != 'DP': | |
313 | return | |
314 | elif self.addr == ADDR_DP_SELECT: | |
315 | self.ctrlsel = self.data & BIT_SELECT_CTRLSEL | |
316 | elif self.addr == ADDR_DP_CTRLSTAT and self.ctrlsel == 0: | |
317 | self.orundetect = self.data & BIT_CTRLSTAT_ORUNDETECT | |
318 | ||
319 | def get_address_description(self): | |
320 | ''' | |
321 | Return a human-readable description of the currently selected address, | |
322 | for annotated results. | |
323 | ''' | |
324 | if self.apdp == 'DP': | |
325 | if self.rw == 'R': | |
326 | # Tables 2-4 & 2-5 in ADIv5.2 spec ARM document IHI 0031C | |
327 | return { | |
328 | 0: 'IDCODE', | |
329 | 0x4: 'R CTRL/STAT' if self.ctrlsel == 0 else 'R DLCR', | |
330 | 0x8: 'RESEND', | |
331 | 0xC: 'RDBUFF' | |
332 | }[self.addr] | |
333 | elif self.rw == 'W': | |
334 | # Tables 2-4 & 2-5 in ADIv5.2 spec ARM document IHI 0031C | |
335 | return { | |
336 | 0: 'W ABORT', | |
337 | 0x4: 'W CTRL/STAT' if self.ctrlsel == 0 else 'W DLCR', | |
338 | 0x8: 'W SELECT', | |
339 | 0xC: 'W RESERVED' | |
340 | }[self.addr] | |
341 | elif self.apdp == 'AP': | |
342 | if self.rw == 'R': | |
343 | return 'R AP%x' % self.addr | |
344 | elif self.rw == 'W': | |
345 | return 'W AP%x' % self.addr | |
346 | ||
347 | # Any legitimate operations shouldn't fall through to here, probably | |
348 | # a decoder bug. | |
349 | return '? %s%s%x' % (self.rw, self.apdp, self.addr) |