]>
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 | |
392a5d1e | 21 | from common.srdhelper import bin2int |
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 | ||
56 | class Ann(object): | |
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' | |
194 | name = 'JTAG / EJTAG (MIPS)' | |
195 | longname = 'Joint Test Action Group / EJTAG (MIPS)' | |
196 | desc = 'MIPS EJTAG protocol.' | |
197 | license = 'gplv2+' | |
198 | inputs = ['jtag'] | |
199 | outputs = ['jtag_ejtag'] | |
200 | annotations = ( | |
201 | ('instruction', 'Instruction'), | |
202 | ) + regs_items['ann'] + ( | |
203 | ('control_field_in', 'Control field in'), | |
204 | ('control_field_out', 'Control field out'), | |
205 | ('pracc', 'PrAcc'), | |
206 | ) | |
207 | annotation_rows = ( | |
208 | ('instructions', 'Instructions', (0,)), | |
209 | ('regs', 'Registers', regs_items['rows_range']), | |
210 | ('control_fields_in', 'Control fields in', (10,)), | |
211 | ('control_fields_out', 'Control fields out', (11,)), | |
212 | ('pracc', 'PrAcc', (12,)), | |
213 | ) | |
214 | ||
215 | def __init__(self): | |
216 | self.reset() | |
217 | ||
218 | def reset(self): | |
219 | self.state = State.RESET | |
220 | self.pracc_state = PraccState() | |
221 | ||
222 | def put_current(self, data): | |
223 | self.put(self.ss, self.es, self.out_ann, data) | |
224 | ||
225 | def put_at(self, ss: int, es: int, data): | |
226 | self.put(ss, es, self.out_ann, data); | |
227 | ||
228 | def start(self): | |
229 | self.out_ann = self.register(srd.OUTPUT_ANN) | |
230 | ||
231 | def select_reg(self, ir_value: int): | |
aaaf37e1 | 232 | self.state = ejtag_state_map.get(ir_value, State.RESET) |
4bfb9af7 VI |
233 | |
234 | def parse_pracc(self): | |
392a5d1e UH |
235 | control_in = bin2int(self.last_data['in']['data'][0]) |
236 | control_out = bin2int(self.last_data['out']['data'][0]) | |
4bfb9af7 VI |
237 | |
238 | # Check if JTAG master acknowledges a pending PrAcc. | |
239 | if not ((not (control_in & ControlReg.PRACC)) and \ | |
240 | (control_out & ControlReg.PRACC)): | |
241 | return | |
242 | ||
d1b7bd1b | 243 | ss, es = self.pracc_state.ss, self.pracc_state.es |
4bfb9af7 VI |
244 | pracc_write = (control_out & ControlReg.PRNW) != 0 |
245 | ||
288e22b0 UH |
246 | s = 'PrAcc: ' |
247 | s += 'Store' if pracc_write else 'Load/Fetch' | |
4bfb9af7 VI |
248 | |
249 | if pracc_write: | |
250 | if self.pracc_state.address_out != None: | |
288e22b0 | 251 | s += ', A:' + ' 0x{:08X}'.format(self.pracc_state.address_out) |
4bfb9af7 | 252 | if self.pracc_state.data_out != None: |
288e22b0 | 253 | s += ', D:' + ' 0x{:08X}'.format(self.pracc_state.data_out) |
4bfb9af7 VI |
254 | else: |
255 | if self.pracc_state.address_out != None: | |
288e22b0 | 256 | s += ', A:' + ' 0x{:08X}'.format(self.pracc_state.address_out) |
4bfb9af7 | 257 | if self.pracc_state.data_in != None: |
288e22b0 | 258 | s += ', D:' + ' 0x{:08X}'.format(self.pracc_state.data_in) |
4bfb9af7 VI |
259 | |
260 | self.pracc_state.reset() | |
261 | ||
288e22b0 | 262 | self.put_at(ss, es, [Ann.PRACC, [s]]) |
4bfb9af7 VI |
263 | |
264 | def parse_control_reg(self, ann): | |
265 | reg_write = ann == Ann.CONTROL_FIELD_IN | |
266 | control_bit_positions = [] | |
aaaf37e1 | 267 | data_select = 'in' if (reg_write) else 'out' |
4bfb9af7 VI |
268 | |
269 | control_bit_positions = self.last_data[data_select]['data'][1] | |
270 | control_data = self.last_data[data_select]['data'][0] | |
271 | ||
272 | # Annotate control register fields. | |
273 | for field in ejtag_control_reg: | |
274 | start_bit = 31 - field[1] | |
275 | end_bit = 31 - field[0] | |
276 | comment = field[2] | |
277 | value_descriptions = [] | |
278 | ||
279 | if reg_write: | |
280 | if len(field[3]) < 2: | |
281 | continue | |
282 | value_descriptions = field[3][1] | |
283 | else: | |
284 | value_descriptions = field[3][0] | |
285 | ||
d1b7bd1b UH |
286 | ss = control_bit_positions[start_bit][0] |
287 | es = control_bit_positions[end_bit][1] | |
4bfb9af7 VI |
288 | |
289 | value_str = control_data[end_bit : start_bit + 1] | |
392a5d1e | 290 | value_index = bin2int(value_str) |
4bfb9af7 VI |
291 | |
292 | short_desc = comment + ': ' + value_str | |
293 | long_desc = value_descriptions[value_index] if len(value_descriptions) > value_index else '?' | |
4bfb9af7 | 294 | |
288e22b0 | 295 | self.put_at(ss, es, [ann, [long_desc, short_desc]]) |
4bfb9af7 VI |
296 | |
297 | def check_last_data(self): | |
298 | if not hasattr(self, 'last_data'): | |
299 | self.last_data = {'in': {}, 'out': {}} | |
300 | ||
301 | def handle_fastdata(self, val, ann): | |
302 | spracc_write_desc = { | |
303 | 0: ['0', 'SPrAcc: 0', 'Request completion of Fastdata access'], | |
304 | 1: ['1', 'SPrAcc: 1', 'No effect'], | |
305 | } | |
306 | spracc_read_desc = { | |
307 | 0: ['0', 'SPrAcc: 0', 'Fastdata access failure'], | |
308 | 1: ['1', 'SPrAcc: 1', 'Successful completion of Fastdata access'], | |
309 | } | |
310 | ||
311 | bitstring = val[0] | |
312 | bit_sample_pos = val[1] | |
313 | fastdata_state = bitstring[32] | |
392a5d1e | 314 | data = bin2int(bitstring[0:32]) |
4bfb9af7 VI |
315 | |
316 | fastdata_bit_pos = bit_sample_pos[32] | |
317 | data_pos = [bit_sample_pos[31][0], bit_sample_pos[0][1]] | |
318 | ||
d1b7bd1b UH |
319 | ss_fastdata, es_fastdata = fastdata_bit_pos |
320 | ss_data, es_data = data_pos | |
4bfb9af7 VI |
321 | |
322 | display_data = [ann, ['0x{:08X}'.format(data)]] | |
323 | spracc_display_data = [] | |
324 | ||
325 | if ann == Ann.CONTROL_FIELD_IN: | |
326 | spracc_display_data = [ann, spracc_write_desc[int(fastdata_state)]] | |
327 | elif ann == Ann.CONTROL_FIELD_OUT: | |
328 | spracc_display_data = [ann, spracc_read_desc[int(fastdata_state)]] | |
329 | ||
d1b7bd1b UH |
330 | self.put_at(ss_fastdata, es_fastdata, spracc_display_data) |
331 | self.put_at(ss_data, es_data, display_data) | |
4bfb9af7 VI |
332 | |
333 | def handle_dr_tdi(self, val): | |
392a5d1e | 334 | value = bin2int(val[0]) |
4bfb9af7 VI |
335 | self.check_last_data() |
336 | self.last_data['in'] = {'ss': self.ss, 'es': self.es, 'data': val} | |
337 | ||
d1b7bd1b | 338 | self.pracc_state.ss, self.pracc_state.es = self.ss, self.es |
4bfb9af7 VI |
339 | |
340 | if self.state == State.ADDRESS: | |
341 | self.pracc_state.address_in = value | |
342 | elif self.state == State.DATA: | |
343 | self.pracc_state.data_in = value | |
344 | elif self.state == State.FASTDATA: | |
345 | self.handle_fastdata(val, Ann.CONTROL_FIELD_IN) | |
346 | ||
347 | def handle_dr_tdo(self, val): | |
392a5d1e | 348 | value = bin2int(val[0]) |
4bfb9af7 VI |
349 | self.check_last_data() |
350 | self.last_data['out'] = {'ss': self.ss, 'es': self.es, 'data': val} | |
351 | if self.state == State.ADDRESS: | |
352 | self.pracc_state.address_out = value | |
353 | elif self.state == State.DATA: | |
354 | self.pracc_state.data_out = value | |
355 | elif self.state == State.FASTDATA: | |
356 | self.handle_fastdata(val, Ann.CONTROL_FIELD_OUT) | |
357 | ||
358 | def handle_ir_tdi(self, val): | |
392a5d1e | 359 | code = bin2int(val[0]) |
4bfb9af7 VI |
360 | hex = '0x{:02X}'.format(code) |
361 | if code in ejtag_insn: | |
362 | # Format instruction name. | |
363 | insn = ejtag_insn[code] | |
364 | s_short = insn[0] | |
365 | s_long = insn[0] + ': ' + insn[1] + ' (' + hex + ')' | |
366 | # Display it and select data register. | |
068960b6 | 367 | self.put_current([Ann.INSTRUCTION, [s_long, s_short]]) |
4bfb9af7 VI |
368 | else: |
369 | self.put_current([Ann.INSTRUCTION, [hex, 'IR TDI ({})'.format(hex)]]) | |
370 | self.select_reg(code) | |
371 | ||
372 | def handle_new_state(self, new_state): | |
373 | if new_state != 'UPDATE-DR' or not hasattr(self, 'last_data'): | |
374 | return | |
375 | ||
376 | if self.state == State.RESET: | |
377 | return | |
378 | ||
379 | reg_name = ejtag_reg[self.state] | |
380 | ann_index = Ann.REGISTER + self.state | |
381 | display_data = [ann_index, [reg_name]] | |
382 | self.put_at(self.last_data['in']['ss'], self.last_data['in']['es'], display_data) | |
383 | ||
384 | if self.state == State.CONTROL: | |
385 | control_bit_positions = self.last_data['in']['data'][1] | |
386 | bit_count = len(control_bit_positions) | |
387 | # Check if control register data length is correct. | |
388 | if bit_count != 32: | |
389 | error_display = [Ann.REGISTER, ['Error: length != 32']] | |
390 | self.put_at(self.last_data['in']['ss'], self.last_data['in']['es'], error_display) | |
391 | return | |
392 | self.parse_control_reg(Ann.CONTROL_FIELD_IN) | |
393 | self.parse_control_reg(Ann.CONTROL_FIELD_OUT) | |
394 | self.parse_pracc() | |
395 | ||
396 | def decode(self, ss: int, es: int, data): | |
397 | cmd, val = data | |
398 | self.ss, self.es = ss, es | |
399 | ||
400 | if cmd == 'IR TDI': | |
401 | self.handle_ir_tdi(val) | |
402 | elif cmd == 'DR TDI': | |
403 | self.handle_dr_tdi(val) | |
404 | elif cmd == 'DR TDO': | |
405 | self.handle_dr_tdo(val) | |
406 | elif cmd == 'NEW STATE': | |
407 | self.handle_new_state(val) |