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