From: Vlad Ivanov Date: Fri, 4 May 2018 06:57:53 +0000 (+0300) Subject: Add MIPS EJTAG decoder X-Git-Url: https://sigrok.org/gitweb/?p=libsigrokdecode.git;a=commitdiff_plain;h=4bfb9af72ff70a63322ce238a0ed23a3ebf8505b;ds=sidebyside Add MIPS EJTAG decoder Signed-off-by: Vlad Ivanov --- diff --git a/decoders/jtag_ejtag/__init__.py b/decoders/jtag_ejtag/__init__.py new file mode 100644 index 0000000..41b3641 --- /dev/null +++ b/decoders/jtag_ejtag/__init__.py @@ -0,0 +1,20 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Vladislav Ivanov +## +## 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 . +## + +from .pd import Decoder diff --git a/decoders/jtag_ejtag/pd.py b/decoders/jtag_ejtag/pd.py new file mode 100644 index 0000000..b479398 --- /dev/null +++ b/decoders/jtag_ejtag/pd.py @@ -0,0 +1,421 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2018 Vladislav Ivanov +## +## 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 . +## + +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)