--- /dev/null
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Vladislav Ivanov <vlad.ivanov@lab-systems.ru>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class Instruction(object):
+ IDCODE = 0x01
+ IMPCODE = 0x03
+ ADDRESS = 0x08
+ DATA = 0x09
+ CONTROL = 0x0A
+ ALL = 0x0B
+ EJTAGBOOT = 0x0C
+ NORMALBOOT = 0x0D
+ FASTDATA = 0x0E
+ TCBCONTROLA = 0x10
+ TCBCONTROLB = 0x11
+ TCBDATA = 0x12
+ TCBCONTROLC = 0x13
+ PCSAMPLE = 0x14
+ TCBCONTROLD = 0x15
+ TCBCONTROLE = 0x16
+
+class State(object):
+ RESET = 0
+ DEVICE_ID = 1
+ IMPLEMENTATION = 2
+ DATA = 3
+ ADDRESS = 4
+ CONTROL = 5
+ FASTDATA = 6
+ PC_SAMPLE = 7
+ BYPASS = 8
+
+class ControlReg(object):
+ PRACC = (1 << 18)
+ PRNW = (1 << 19)
+
+class Ann(object):
+ INSTRUCTION = 0
+ REGISTER = 1
+ CONTROL_FIELD_IN = 10
+ CONTROL_FIELD_OUT = 11
+ PRACC = 12
+
+ejtag_insn = {
+ 0x00: ['Free', 'Boundary scan'],
+ 0x01: ['IDCODE', 'Select Device Identification (ID) register'],
+ 0x02: ['Free', 'Boundary scan'],
+ 0x03: ['IMPCODE', 'Select Implementation register'],
+ 0x08: ['ADDRESS', 'Select Address register'],
+ 0x09: ['DATA', 'Select Data register'],
+ 0x0A: ['CONTROL', 'Select EJTAG Control register'],
+ 0x0B: ['ALL', 'Select the Address, Data and EJTAG Control registers'],
+ 0x0C: ['EJTAGBOOT', 'Fetch code from the debug exception vector after reset'],
+ 0x0D: ['NORMALBOOT', 'Execute the reset handler after reset'],
+ 0x0E: ['FASTDATA', 'Select the Data and Fastdata registers'],
+ 0x0F: ['Reserved', 'Reserved'],
+ 0x10: ['TCBCONTROLA', 'Select the control register TCBTraceControl'],
+ 0x11: ['TCBCONTROLB', 'Selects trace control block register B'],
+ 0x12: ['TCBDATA', 'Access the registers specified by TCBCONTROLB'],
+ 0x13: ['TCBCONTROLC', 'Select trace control block register C'],
+ 0x14: ['PCSAMPLE', 'Select the PCsample register'],
+ 0x15: ['TCBCONTROLD', 'Select trace control block register D'],
+ 0x16: ['TCBCONTROLE', 'Select trace control block register E'],
+ 0x17: ['FDC', 'Select Fast Debug Channel'],
+ 0x1C: ['Free', 'Boundary scan'],
+}
+
+ejtag_reg = {
+ 0x00: 'RESET',
+ 0x01: 'DEVICE_ID',
+ 0x02: 'IMPLEMENTATION',
+ 0x03: 'DATA',
+ 0x04: 'ADDRESS',
+ 0x05: 'CONTROL',
+ 0x06: 'FASTDATA',
+ 0x07: 'PC_SAMPLE',
+ 0x08: 'BYPASS',
+}
+
+ejtag_control_reg = [
+ [31, 31, 'Rocc', [
+ # Read
+ ['No reset ocurred', 'Reset ocurred'],
+ # Write
+ ['Acknowledge reset', 'No effect'],
+ ]],
+ [30, 29, 'Psz', [
+ ['Access: byte', 'Access: halfword', 'Access: word', 'Access: triple'],
+ ]],
+ [23, 23, 'VPED', [
+ ['VPE disabled', 'VPE enabled'],
+ ]],
+ [22, 22, 'Doze', [
+ ['Processor is not in low-power mode', 'Processor is in low-power mode'],
+ ]],
+ [21, 21, 'Halt', [
+ ['Internal system bus clock is running', 'Internal system bus clock is stopped'],
+ ]],
+ [20, 20, 'Per Rst', [
+ ['No peripheral reset applied', 'Peripheral reset applied'],
+ ['Deassert peripheral reset', 'Assert peripheral reset'],
+ ]],
+ [19, 19, 'PRn W', [
+ ['Read processor access', 'Write processor access'],
+ ]],
+ [18, 18, 'Pr Acc', [
+ ['No pending processor access', 'Pending processor access'],
+ ['Finish processor access', 'Don\'t finish processor access'],
+ ]],
+ [16, 16, 'Pr Rst', [
+ ['No processor reset applied', 'Processor reset applied'],
+ ['Deassert processor reset', 'Assert system reset'],
+ ]],
+ [15, 15, 'Prob En', [
+ ['Probe will not serve processor accesses', 'Probe will service processor accesses'],
+ ]],
+ [14, 14, 'Prob Trap', [
+ ['Default location', 'DMSEG fetch'],
+ ['Set to default location', 'Set to DMSEG fetch'],
+ ]],
+ [13, 13, 'ISA On Debug', [
+ ['MIPS32/MIPS64 ISA', 'microMIPS ISA'],
+ ['Set to MIPS32/MIPS64 ISA', 'Set to microMIPS ISA'],
+ ]],
+ [12, 12, 'EJTAG Brk', [
+ ['No pending debug interrupt', 'Pending debug interrupt'],
+ ['No effect', 'Request debug interrupt'],
+ ]],
+ [3, 3, 'DM', [
+ ['Not in debug mode', 'In debug mode'],
+ ]],
+]
+
+ejtag_state_map = {
+ Instruction.IDCODE: State.DEVICE_ID,
+ Instruction.IMPCODE: State.IMPLEMENTATION,
+ Instruction.DATA: State.DATA,
+ Instruction.ADDRESS: State.ADDRESS,
+ Instruction.CONTROL: State.CONTROL,
+ Instruction.FASTDATA: State.FASTDATA,
+}
+
+def bin_to_int(s: str):
+ return int('0b' + s, 2)
+
+class RegData(object):
+ def __init__(self):
+ self.ss = None
+ self.es = None
+ self.data = None
+
+class LastData(object):
+ def __init__(self):
+ self.data_in = RegData()
+ self.data_out = RegData()
+
+class PraccState(object):
+ def reset(self):
+ self.address_in = None
+ self.address_out = None
+ self.data_in = None
+ self.data_out = None
+ self.write = False
+ self.start_sample = 0
+ self.end_sample = 0
+
+ def __init__(self):
+ self.reset()
+
+regs_items = {
+ 'ann': tuple([tuple([s.lower(), s]) for s in list(ejtag_reg.values())]),
+ 'rows_range': tuple(range(1, 1 + 9)),
+}
+
+class Decoder(srd.Decoder):
+ api_version = 3
+ id = 'jtag_ejtag'
+ name = 'JTAG / EJTAG (MIPS)'
+ longname = 'Joint Test Action Group / EJTAG (MIPS)'
+ desc = 'MIPS EJTAG protocol.'
+ license = 'gplv2+'
+ inputs = ['jtag']
+ outputs = ['jtag_ejtag']
+ annotations = (
+ ('instruction', 'Instruction'),
+ ) + regs_items['ann'] + (
+ ('control_field_in', 'Control field in'),
+ ('control_field_out', 'Control field out'),
+ ('pracc', 'PrAcc'),
+ )
+ annotation_rows = (
+ ('instructions', 'Instructions', (0,)),
+ ('regs', 'Registers', regs_items['rows_range']),
+ ('control_fields_in', 'Control fields in', (10,)),
+ ('control_fields_out', 'Control fields out', (11,)),
+ ('pracc', 'PrAcc', (12,)),
+ )
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.state = State.RESET
+ self.pracc_state = PraccState()
+
+ def put_current(self, data):
+ self.put(self.ss, self.es, self.out_ann, data)
+
+ def put_at(self, ss: int, es: int, data):
+ self.put(ss, es, self.out_ann, data);
+
+ def start(self):
+ self.out_ann = self.register(srd.OUTPUT_ANN)
+
+ def select_reg(self, ir_value: int):
+ if ir_value in ejtag_state_map:
+ self.state = ejtag_state_map[ir_value]
+ else:
+ self.state = State.RESET
+
+ def parse_pracc(self):
+ control_in = bin_to_int(self.last_data['in']['data'][0])
+ control_out = bin_to_int(self.last_data['out']['data'][0])
+
+ # Check if JTAG master acknowledges a pending PrAcc.
+ if not ((not (control_in & ControlReg.PRACC)) and \
+ (control_out & ControlReg.PRACC)):
+ return
+
+ start_sample = self.pracc_state.start_sample
+ end_sample = self.pracc_state.end_sample
+ pracc_write = (control_out & ControlReg.PRNW) != 0
+
+ display_string = 'PrAcc: '
+ display_string += 'Store' if pracc_write else 'Load/Fetch'
+
+ if pracc_write:
+ if self.pracc_state.address_out != None:
+ display_string += ', A:' + ' 0x{:08X}'.format(self.pracc_state.address_out)
+ if self.pracc_state.data_out != None:
+ display_string += ', D:' + ' 0x{:08X}'.format(self.pracc_state.data_out)
+ else:
+ if self.pracc_state.address_out != None:
+ display_string += ', A:' + ' 0x{:08X}'.format(self.pracc_state.address_out)
+ if self.pracc_state.data_in != None:
+ display_string += ', D:' + ' 0x{:08X}'.format(self.pracc_state.data_in)
+
+ self.pracc_state.reset()
+
+ display_data = [Ann.PRACC, [display_string]]
+ self.put_at(start_sample, end_sample, display_data)
+
+ def parse_control_reg(self, ann):
+ reg_write = ann == Ann.CONTROL_FIELD_IN
+ control_bit_positions = []
+ data_select = ''
+
+ if reg_write:
+ data_select = 'in'
+ else:
+ data_select = 'out'
+
+ control_bit_positions = self.last_data[data_select]['data'][1]
+ control_data = self.last_data[data_select]['data'][0]
+
+ # Annotate control register fields.
+ for field in ejtag_control_reg:
+ start_bit = 31 - field[1]
+ end_bit = 31 - field[0]
+ comment = field[2]
+ value_descriptions = []
+
+ if reg_write:
+ if len(field[3]) < 2:
+ continue
+ value_descriptions = field[3][1]
+ else:
+ value_descriptions = field[3][0]
+
+ start_sample = control_bit_positions[start_bit][0]
+ end_sample = control_bit_positions[end_bit][1]
+
+ value_str = control_data[end_bit : start_bit + 1]
+ value_index = bin_to_int(value_str)
+
+ short_desc = comment + ': ' + value_str
+ long_desc = value_descriptions[value_index] if len(value_descriptions) > value_index else '?'
+ display_data = [ann, [short_desc, long_desc]]
+
+ self.put_at(start_sample, end_sample, display_data)
+
+ def check_last_data(self):
+ if not hasattr(self, 'last_data'):
+ self.last_data = {'in': {}, 'out': {}}
+
+ def handle_fastdata(self, val, ann):
+ spracc_write_desc = {
+ 0: ['0', 'SPrAcc: 0', 'Request completion of Fastdata access'],
+ 1: ['1', 'SPrAcc: 1', 'No effect'],
+ }
+ spracc_read_desc = {
+ 0: ['0', 'SPrAcc: 0', 'Fastdata access failure'],
+ 1: ['1', 'SPrAcc: 1', 'Successful completion of Fastdata access'],
+ }
+
+ bitstring = val[0]
+ bit_sample_pos = val[1]
+ fastdata_state = bitstring[32]
+ data = bin_to_int(bitstring[0:32])
+
+ fastdata_bit_pos = bit_sample_pos[32]
+ data_pos = [bit_sample_pos[31][0], bit_sample_pos[0][1]]
+
+ fastdata_sample_start, fastdata_sample_end = fastdata_bit_pos
+ data_sample_start, data_sample_end = data_pos
+
+ display_data = [ann, ['0x{:08X}'.format(data)]]
+ spracc_display_data = []
+
+ if ann == Ann.CONTROL_FIELD_IN:
+ spracc_display_data = [ann, spracc_write_desc[int(fastdata_state)]]
+ elif ann == Ann.CONTROL_FIELD_OUT:
+ spracc_display_data = [ann, spracc_read_desc[int(fastdata_state)]]
+
+ self.put_at(fastdata_sample_start, fastdata_sample_end, spracc_display_data)
+ self.put_at(data_sample_start, data_sample_end, display_data)
+
+ def handle_dr_tdi(self, val):
+ value = bin_to_int(val[0])
+ self.check_last_data()
+ self.last_data['in'] = {'ss': self.ss, 'es': self.es, 'data': val}
+
+ self.pracc_state.start_sample = self.ss
+ self.pracc_state.end_sample = self.es
+
+ if self.state == State.ADDRESS:
+ self.pracc_state.address_in = value
+ elif self.state == State.DATA:
+ self.pracc_state.data_in = value
+ elif self.state == State.FASTDATA:
+ self.handle_fastdata(val, Ann.CONTROL_FIELD_IN)
+
+ def handle_dr_tdo(self, val):
+ value = bin_to_int(val[0])
+ self.check_last_data()
+ self.last_data['out'] = {'ss': self.ss, 'es': self.es, 'data': val}
+ if self.state == State.ADDRESS:
+ self.pracc_state.address_out = value
+ elif self.state == State.DATA:
+ self.pracc_state.data_out = value
+ elif self.state == State.FASTDATA:
+ self.handle_fastdata(val, Ann.CONTROL_FIELD_OUT)
+
+ def handle_ir_tdi(self, val):
+ code = bin_to_int(val[0])
+ hex = '0x{:02X}'.format(code)
+ if code in ejtag_insn:
+ # Format instruction name.
+ insn = ejtag_insn[code]
+ s_short = insn[0]
+ s_long = insn[0] + ': ' + insn[1] + ' (' + hex + ')'
+ # Display it and select data register.
+ self.put_current([Ann.INSTRUCTION, [s_short, s_long]])
+ else:
+ self.put_current([Ann.INSTRUCTION, [hex, 'IR TDI ({})'.format(hex)]])
+ self.select_reg(code)
+
+ def handle_new_state(self, new_state):
+ if new_state != 'UPDATE-DR' or not hasattr(self, 'last_data'):
+ return
+
+ if self.state == State.RESET:
+ return
+
+ reg_name = ejtag_reg[self.state]
+ ann_index = Ann.REGISTER + self.state
+ display_data = [ann_index, [reg_name]]
+ self.put_at(self.last_data['in']['ss'], self.last_data['in']['es'], display_data)
+
+ if self.state == State.CONTROL:
+ control_bit_positions = self.last_data['in']['data'][1]
+ bit_count = len(control_bit_positions)
+ # Check if control register data length is correct.
+ if bit_count != 32:
+ error_display = [Ann.REGISTER, ['Error: length != 32']]
+ self.put_at(self.last_data['in']['ss'], self.last_data['in']['es'], error_display)
+ return
+ self.parse_control_reg(Ann.CONTROL_FIELD_IN)
+ self.parse_control_reg(Ann.CONTROL_FIELD_OUT)
+ self.parse_pracc()
+
+ def decode(self, ss: int, es: int, data):
+ cmd, val = data
+ self.ss, self.es = ss, es
+
+ if cmd == 'IR TDI':
+ self.handle_ir_tdi(val)
+ elif cmd == 'DR TDI':
+ self.handle_dr_tdi(val)
+ elif cmd == 'DR TDO':
+ self.handle_dr_tdo(val)
+ elif cmd == 'NEW STATE':
+ self.handle_new_state(val)