]>
Commit | Line | Data |
---|---|---|
4bfb9af7 VI |
1 | ## |
2 | ## This file is part of the libsigrokdecode project. | |
3 | ## | |
4 | ## Copyright (C) 2018 Vladislav Ivanov <vlad.ivanov@lab-systems.ru> | |
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 | |
923b4507 | 21 | from common.srdhelper import bin2int, SrdIntEnum |
4bfb9af7 VI |
22 | |
23 | class Instruction(object): | |
24 | IDCODE = 0x01 | |
25 | IMPCODE = 0x03 | |
26 | ADDRESS = 0x08 | |
27 | DATA = 0x09 | |
28 | CONTROL = 0x0A | |
29 | ALL = 0x0B | |
30 | EJTAGBOOT = 0x0C | |
31 | NORMALBOOT = 0x0D | |
32 | FASTDATA = 0x0E | |
33 | TCBCONTROLA = 0x10 | |
34 | TCBCONTROLB = 0x11 | |
35 | TCBDATA = 0x12 | |
36 | TCBCONTROLC = 0x13 | |
37 | PCSAMPLE = 0x14 | |
38 | TCBCONTROLD = 0x15 | |
39 | TCBCONTROLE = 0x16 | |
40 | ||
41 | class State(object): | |
42 | RESET = 0 | |
43 | DEVICE_ID = 1 | |
44 | IMPLEMENTATION = 2 | |
45 | DATA = 3 | |
46 | ADDRESS = 4 | |
47 | CONTROL = 5 | |
48 | FASTDATA = 6 | |
49 | PC_SAMPLE = 7 | |
50 | BYPASS = 8 | |
51 | ||
52 | class ControlReg(object): | |
53 | PRACC = (1 << 18) | |
54 | PRNW = (1 << 19) | |
55 | ||
923b4507 | 56 | class Ann(SrdIntEnum): |
4bfb9af7 VI |
57 | INSTRUCTION = 0 |
58 | REGISTER = 1 | |
59 | CONTROL_FIELD_IN = 10 | |
60 | CONTROL_FIELD_OUT = 11 | |
61 | PRACC = 12 | |
62 | ||
63 | ejtag_insn = { | |
64 | 0x00: ['Free', 'Boundary scan'], | |
65 | 0x01: ['IDCODE', 'Select Device Identification (ID) register'], | |
66 | 0x02: ['Free', 'Boundary scan'], | |
67 | 0x03: ['IMPCODE', 'Select Implementation register'], | |
68 | 0x08: ['ADDRESS', 'Select Address register'], | |
69 | 0x09: ['DATA', 'Select Data register'], | |
70 | 0x0A: ['CONTROL', 'Select EJTAG Control register'], | |
71 | 0x0B: ['ALL', 'Select the Address, Data and EJTAG Control registers'], | |
72 | 0x0C: ['EJTAGBOOT', 'Fetch code from the debug exception vector after reset'], | |
73 | 0x0D: ['NORMALBOOT', 'Execute the reset handler after reset'], | |
74 | 0x0E: ['FASTDATA', 'Select the Data and Fastdata registers'], | |
75 | 0x0F: ['Reserved', 'Reserved'], | |
76 | 0x10: ['TCBCONTROLA', 'Select the control register TCBTraceControl'], | |
77 | 0x11: ['TCBCONTROLB', 'Selects trace control block register B'], | |
78 | 0x12: ['TCBDATA', 'Access the registers specified by TCBCONTROLB'], | |
79 | 0x13: ['TCBCONTROLC', 'Select trace control block register C'], | |
80 | 0x14: ['PCSAMPLE', 'Select the PCsample register'], | |
81 | 0x15: ['TCBCONTROLD', 'Select trace control block register D'], | |
82 | 0x16: ['TCBCONTROLE', 'Select trace control block register E'], | |
83 | 0x17: ['FDC', 'Select Fast Debug Channel'], | |
84 | 0x1C: ['Free', 'Boundary scan'], | |
85 | } | |
86 | ||
87 | ejtag_reg = { | |
88 | 0x00: 'RESET', | |
89 | 0x01: 'DEVICE_ID', | |
90 | 0x02: 'IMPLEMENTATION', | |
91 | 0x03: 'DATA', | |
92 | 0x04: 'ADDRESS', | |
93 | 0x05: 'CONTROL', | |
94 | 0x06: 'FASTDATA', | |
95 | 0x07: 'PC_SAMPLE', | |
96 | 0x08: 'BYPASS', | |
97 | } | |
98 | ||
99 | ejtag_control_reg = [ | |
100 | [31, 31, 'Rocc', [ | |
101 | # Read | |
102 | ['No reset ocurred', 'Reset ocurred'], | |
103 | # Write | |
104 | ['Acknowledge reset', 'No effect'], | |
105 | ]], | |
106 | [30, 29, 'Psz', [ | |
107 | ['Access: byte', 'Access: halfword', 'Access: word', 'Access: triple'], | |
108 | ]], | |
109 | [23, 23, 'VPED', [ | |
110 | ['VPE disabled', 'VPE enabled'], | |
111 | ]], | |
112 | [22, 22, 'Doze', [ | |
113 | ['Processor is not in low-power mode', 'Processor is in low-power mode'], | |
114 | ]], | |
115 | [21, 21, 'Halt', [ | |
116 | ['Internal system bus clock is running', 'Internal system bus clock is stopped'], | |
117 | ]], | |
118 | [20, 20, 'Per Rst', [ | |
119 | ['No peripheral reset applied', 'Peripheral reset applied'], | |
120 | ['Deassert peripheral reset', 'Assert peripheral reset'], | |
121 | ]], | |
122 | [19, 19, 'PRn W', [ | |
123 | ['Read processor access', 'Write processor access'], | |
124 | ]], | |
125 | [18, 18, 'Pr Acc', [ | |
126 | ['No pending processor access', 'Pending processor access'], | |
127 | ['Finish processor access', 'Don\'t finish processor access'], | |
128 | ]], | |
129 | [16, 16, 'Pr Rst', [ | |
130 | ['No processor reset applied', 'Processor reset applied'], | |
131 | ['Deassert processor reset', 'Assert system reset'], | |
132 | ]], | |
133 | [15, 15, 'Prob En', [ | |
134 | ['Probe will not serve processor accesses', 'Probe will service processor accesses'], | |
135 | ]], | |
136 | [14, 14, 'Prob Trap', [ | |
137 | ['Default location', 'DMSEG fetch'], | |
138 | ['Set to default location', 'Set to DMSEG fetch'], | |
139 | ]], | |
140 | [13, 13, 'ISA On Debug', [ | |
141 | ['MIPS32/MIPS64 ISA', 'microMIPS ISA'], | |
142 | ['Set to MIPS32/MIPS64 ISA', 'Set to microMIPS ISA'], | |
143 | ]], | |
144 | [12, 12, 'EJTAG Brk', [ | |
145 | ['No pending debug interrupt', 'Pending debug interrupt'], | |
146 | ['No effect', 'Request debug interrupt'], | |
147 | ]], | |
148 | [3, 3, 'DM', [ | |
149 | ['Not in debug mode', 'In debug mode'], | |
150 | ]], | |
151 | ] | |
152 | ||
153 | ejtag_state_map = { | |
154 | Instruction.IDCODE: State.DEVICE_ID, | |
155 | Instruction.IMPCODE: State.IMPLEMENTATION, | |
156 | Instruction.DATA: State.DATA, | |
157 | Instruction.ADDRESS: State.ADDRESS, | |
158 | Instruction.CONTROL: State.CONTROL, | |
159 | Instruction.FASTDATA: State.FASTDATA, | |
160 | } | |
161 | ||
4bfb9af7 VI |
162 | class RegData(object): |
163 | def __init__(self): | |
164 | self.ss = None | |
165 | self.es = None | |
166 | self.data = None | |
167 | ||
168 | class LastData(object): | |
169 | def __init__(self): | |
170 | self.data_in = RegData() | |
171 | self.data_out = RegData() | |
172 | ||
173 | class PraccState(object): | |
174 | def reset(self): | |
175 | self.address_in = None | |
176 | self.address_out = None | |
177 | self.data_in = None | |
178 | self.data_out = None | |
179 | self.write = False | |
d1b7bd1b UH |
180 | self.ss = 0 |
181 | self.es = 0 | |
4bfb9af7 VI |
182 | |
183 | def __init__(self): | |
184 | self.reset() | |
185 | ||
186 | regs_items = { | |
187 | 'ann': tuple([tuple([s.lower(), s]) for s in list(ejtag_reg.values())]), | |
188 | 'rows_range': tuple(range(1, 1 + 9)), | |
189 | } | |
190 | ||
191 | class Decoder(srd.Decoder): | |
192 | api_version = 3 | |
193 | id = 'jtag_ejtag' | |
2787cf2a | 194 | name = 'JTAG / EJTAG' |
4bfb9af7 VI |
195 | longname = 'Joint Test Action Group / EJTAG (MIPS)' |
196 | desc = 'MIPS EJTAG protocol.' | |
197 | license = 'gplv2+' | |
198 | inputs = ['jtag'] | |
6cbba91f | 199 | outputs = [] |
d6d8a8a4 | 200 | tags = ['Debug/trace'] |
4bfb9af7 VI |
201 | annotations = ( |
202 | ('instruction', 'Instruction'), | |
203 | ) + regs_items['ann'] + ( | |
204 | ('control_field_in', 'Control field in'), | |
205 | ('control_field_out', 'Control field out'), | |
206 | ('pracc', 'PrAcc'), | |
207 | ) | |
208 | annotation_rows = ( | |
209 | ('instructions', 'Instructions', (0,)), | |
4bfb9af7 VI |
210 | ('control_fields_in', 'Control fields in', (10,)), |
211 | ('control_fields_out', 'Control fields out', (11,)), | |
9d09d6ed | 212 | ('regs', 'Registers', regs_items['rows_range']), |
e144452b | 213 | ('pracc-vals', 'PrAcc', (12,)), |
4bfb9af7 VI |
214 | ) |
215 | ||
216 | def __init__(self): | |
217 | self.reset() | |
218 | ||
219 | def reset(self): | |
220 | self.state = State.RESET | |
221 | self.pracc_state = PraccState() | |
222 | ||
223 | def put_current(self, data): | |
224 | self.put(self.ss, self.es, self.out_ann, data) | |
225 | ||
226 | def put_at(self, ss: int, es: int, data): | |
f78b814d | 227 | self.put(ss, es, self.out_ann, data) |
4bfb9af7 VI |
228 | |
229 | def start(self): | |
230 | self.out_ann = self.register(srd.OUTPUT_ANN) | |
231 | ||
232 | def select_reg(self, ir_value: int): | |
aaaf37e1 | 233 | self.state = ejtag_state_map.get(ir_value, State.RESET) |
4bfb9af7 VI |
234 | |
235 | def parse_pracc(self): | |
392a5d1e UH |
236 | control_in = bin2int(self.last_data['in']['data'][0]) |
237 | control_out = bin2int(self.last_data['out']['data'][0]) | |
4bfb9af7 VI |
238 | |
239 | # Check if JTAG master acknowledges a pending PrAcc. | |
240 | if not ((not (control_in & ControlReg.PRACC)) and \ | |
241 | (control_out & ControlReg.PRACC)): | |
242 | return | |
243 | ||
d1b7bd1b | 244 | ss, es = self.pracc_state.ss, self.pracc_state.es |
4bfb9af7 VI |
245 | pracc_write = (control_out & ControlReg.PRNW) != 0 |
246 | ||
288e22b0 UH |
247 | s = 'PrAcc: ' |
248 | s += 'Store' if pracc_write else 'Load/Fetch' | |
4bfb9af7 VI |
249 | |
250 | if pracc_write: | |
44c2ae6e | 251 | if self.pracc_state.address_out is not None: |
288e22b0 | 252 | s += ', A:' + ' 0x{:08X}'.format(self.pracc_state.address_out) |
44c2ae6e | 253 | if self.pracc_state.data_out is not None: |
288e22b0 | 254 | s += ', D:' + ' 0x{:08X}'.format(self.pracc_state.data_out) |
4bfb9af7 | 255 | else: |
44c2ae6e | 256 | if self.pracc_state.address_out is not None: |
288e22b0 | 257 | s += ', A:' + ' 0x{:08X}'.format(self.pracc_state.address_out) |
44c2ae6e | 258 | if self.pracc_state.data_in is not None: |
288e22b0 | 259 | s += ', D:' + ' 0x{:08X}'.format(self.pracc_state.data_in) |
4bfb9af7 VI |
260 | |
261 | self.pracc_state.reset() | |
262 | ||
288e22b0 | 263 | self.put_at(ss, es, [Ann.PRACC, [s]]) |
4bfb9af7 VI |
264 | |
265 | def parse_control_reg(self, ann): | |
266 | reg_write = ann == Ann.CONTROL_FIELD_IN | |
267 | control_bit_positions = [] | |
aaaf37e1 | 268 | data_select = 'in' if (reg_write) else 'out' |
4bfb9af7 VI |
269 | |
270 | control_bit_positions = self.last_data[data_select]['data'][1] | |
271 | control_data = self.last_data[data_select]['data'][0] | |
272 | ||
273 | # Annotate control register fields. | |
274 | for field in ejtag_control_reg: | |
275 | start_bit = 31 - field[1] | |
276 | end_bit = 31 - field[0] | |
277 | comment = field[2] | |
278 | value_descriptions = [] | |
279 | ||
280 | if reg_write: | |
281 | if len(field[3]) < 2: | |
282 | continue | |
283 | value_descriptions = field[3][1] | |
284 | else: | |
285 | value_descriptions = field[3][0] | |
286 | ||
d1b7bd1b UH |
287 | ss = control_bit_positions[start_bit][0] |
288 | es = control_bit_positions[end_bit][1] | |
4bfb9af7 VI |
289 | |
290 | value_str = control_data[end_bit : start_bit + 1] | |
392a5d1e | 291 | value_index = bin2int(value_str) |
4bfb9af7 VI |
292 | |
293 | short_desc = comment + ': ' + value_str | |
294 | long_desc = value_descriptions[value_index] if len(value_descriptions) > value_index else '?' | |
4bfb9af7 | 295 | |
288e22b0 | 296 | self.put_at(ss, es, [ann, [long_desc, short_desc]]) |
4bfb9af7 VI |
297 | |
298 | def check_last_data(self): | |
299 | if not hasattr(self, 'last_data'): | |
300 | self.last_data = {'in': {}, 'out': {}} | |
301 | ||
302 | def handle_fastdata(self, val, ann): | |
303 | spracc_write_desc = { | |
304 | 0: ['0', 'SPrAcc: 0', 'Request completion of Fastdata access'], | |
305 | 1: ['1', 'SPrAcc: 1', 'No effect'], | |
306 | } | |
307 | spracc_read_desc = { | |
308 | 0: ['0', 'SPrAcc: 0', 'Fastdata access failure'], | |
309 | 1: ['1', 'SPrAcc: 1', 'Successful completion of Fastdata access'], | |
310 | } | |
311 | ||
312 | bitstring = val[0] | |
313 | bit_sample_pos = val[1] | |
314 | fastdata_state = bitstring[32] | |
392a5d1e | 315 | data = bin2int(bitstring[0:32]) |
4bfb9af7 VI |
316 | |
317 | fastdata_bit_pos = bit_sample_pos[32] | |
318 | data_pos = [bit_sample_pos[31][0], bit_sample_pos[0][1]] | |
319 | ||
d1b7bd1b UH |
320 | ss_fastdata, es_fastdata = fastdata_bit_pos |
321 | ss_data, es_data = data_pos | |
4bfb9af7 VI |
322 | |
323 | display_data = [ann, ['0x{:08X}'.format(data)]] | |
324 | spracc_display_data = [] | |
325 | ||
326 | if ann == Ann.CONTROL_FIELD_IN: | |
327 | spracc_display_data = [ann, spracc_write_desc[int(fastdata_state)]] | |
328 | elif ann == Ann.CONTROL_FIELD_OUT: | |
329 | spracc_display_data = [ann, spracc_read_desc[int(fastdata_state)]] | |
330 | ||
d1b7bd1b UH |
331 | self.put_at(ss_fastdata, es_fastdata, spracc_display_data) |
332 | self.put_at(ss_data, es_data, display_data) | |
4bfb9af7 VI |
333 | |
334 | def handle_dr_tdi(self, val): | |
392a5d1e | 335 | value = bin2int(val[0]) |
4bfb9af7 VI |
336 | self.check_last_data() |
337 | self.last_data['in'] = {'ss': self.ss, 'es': self.es, 'data': val} | |
338 | ||
d1b7bd1b | 339 | self.pracc_state.ss, self.pracc_state.es = self.ss, self.es |
4bfb9af7 VI |
340 | |
341 | if self.state == State.ADDRESS: | |
342 | self.pracc_state.address_in = value | |
343 | elif self.state == State.DATA: | |
344 | self.pracc_state.data_in = value | |
345 | elif self.state == State.FASTDATA: | |
346 | self.handle_fastdata(val, Ann.CONTROL_FIELD_IN) | |
347 | ||
348 | def handle_dr_tdo(self, val): | |
392a5d1e | 349 | value = bin2int(val[0]) |
4bfb9af7 VI |
350 | self.check_last_data() |
351 | self.last_data['out'] = {'ss': self.ss, 'es': self.es, 'data': val} | |
352 | if self.state == State.ADDRESS: | |
353 | self.pracc_state.address_out = value | |
354 | elif self.state == State.DATA: | |
355 | self.pracc_state.data_out = value | |
356 | elif self.state == State.FASTDATA: | |
357 | self.handle_fastdata(val, Ann.CONTROL_FIELD_OUT) | |
358 | ||
359 | def handle_ir_tdi(self, val): | |
392a5d1e | 360 | code = bin2int(val[0]) |
49d3e6f8 | 361 | hexval = '0x{:02X}'.format(code) |
4bfb9af7 VI |
362 | if code in ejtag_insn: |
363 | # Format instruction name. | |
364 | insn = ejtag_insn[code] | |
365 | s_short = insn[0] | |
49d3e6f8 | 366 | s_long = insn[0] + ': ' + insn[1] + ' (' + hexval + ')' |
4bfb9af7 | 367 | # Display it and select data register. |
068960b6 | 368 | self.put_current([Ann.INSTRUCTION, [s_long, s_short]]) |
4bfb9af7 | 369 | else: |
49d3e6f8 | 370 | self.put_current([Ann.INSTRUCTION, [hexval, 'IR TDI ({})'.format(hexval)]]) |
4bfb9af7 VI |
371 | self.select_reg(code) |
372 | ||
373 | def handle_new_state(self, new_state): | |
374 | if new_state != 'UPDATE-DR' or not hasattr(self, 'last_data'): | |
375 | return | |
376 | ||
377 | if self.state == State.RESET: | |
378 | return | |
379 | ||
380 | reg_name = ejtag_reg[self.state] | |
381 | ann_index = Ann.REGISTER + self.state | |
382 | display_data = [ann_index, [reg_name]] | |
383 | self.put_at(self.last_data['in']['ss'], self.last_data['in']['es'], display_data) | |
384 | ||
385 | if self.state == State.CONTROL: | |
386 | control_bit_positions = self.last_data['in']['data'][1] | |
387 | bit_count = len(control_bit_positions) | |
388 | # Check if control register data length is correct. | |
389 | if bit_count != 32: | |
390 | error_display = [Ann.REGISTER, ['Error: length != 32']] | |
391 | self.put_at(self.last_data['in']['ss'], self.last_data['in']['es'], error_display) | |
392 | return | |
393 | self.parse_control_reg(Ann.CONTROL_FIELD_IN) | |
394 | self.parse_control_reg(Ann.CONTROL_FIELD_OUT) | |
395 | self.parse_pracc() | |
396 | ||
397 | def decode(self, ss: int, es: int, data): | |
398 | cmd, val = data | |
399 | self.ss, self.es = ss, es | |
400 | ||
401 | if cmd == 'IR TDI': | |
402 | self.handle_ir_tdi(val) | |
403 | elif cmd == 'DR TDI': | |
404 | self.handle_dr_tdi(val) | |
405 | elif cmd == 'DR TDO': | |
406 | self.handle_dr_tdo(val) | |
407 | elif cmd == 'NEW STATE': | |
408 | self.handle_new_state(val) |