|
| 1 | +## |
| 2 | +## This file is part of the libsigrokdecode project. |
| 3 | +## |
| 4 | +## Copyright (C) 2022 Sergey Spivak <sespivak@yandex.ru> |
| 5 | +## |
| 6 | +## Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | +## of this software and associated documentation files (the "Software"), to deal |
| 8 | +## in the Software without restriction, including without limitation the rights |
| 9 | +## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 10 | +## copies of the Software, and to permit persons to whom the Software is |
| 11 | +## furnished to do so, subject to the following conditions: |
| 12 | +## |
| 13 | +## The above copyright notice and this permission notice shall be included in all |
| 14 | +## copies or substantial portions of the Software. |
| 15 | +## |
| 16 | +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 19 | +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 21 | +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 22 | +## SOFTWARE. |
| 23 | + |
| 24 | +from collections import deque |
| 25 | +import sigrokdecode as srd |
| 26 | + |
| 27 | +''' |
| 28 | +OUTPUT_PYTHON format: |
| 29 | +
|
| 30 | +Packet: |
| 31 | +[<ptype>, <pdata>] |
| 32 | +
|
| 33 | +<ptype>: |
| 34 | + - 'PACKET READ' (ADDRESS READ followed by one or several |
| 35 | + DATA bytes until STOP or START REPEAT) |
| 36 | + - 'PACKET WRITE' (ADDRESS WRITE followed by one or several |
| 37 | + DATA bytes until STOP or START REPEAT) |
| 38 | +
|
| 39 | +<pdata> is the tuple with slave address byte and tuple with data bytes. |
| 40 | +Slave addresses do not include bit 0 (the READ/WRITE indication bit). |
| 41 | +''' |
| 42 | + |
| 43 | +class Ann: |
| 44 | + PACKET_READ, \ |
| 45 | + PACKET_WRITE, \ |
| 46 | + = range(2) |
| 47 | + |
| 48 | +RX = 0 |
| 49 | +TX = 1 |
| 50 | + |
| 51 | +class Decoder(srd.Decoder): |
| 52 | + api_version = 3 |
| 53 | + id = 'i2c_packet' |
| 54 | + name = 'I²C packet' |
| 55 | + longname = 'I²C packet builder' |
| 56 | + desc = 'Combine I²C data to packets' |
| 57 | + license = 'mit' |
| 58 | + inputs = ['i2c'] |
| 59 | + outputs = [] |
| 60 | + tags = ['Embedded/industrial'] |
| 61 | + options = ( |
| 62 | + {'id': 'format', 'desc': 'Data format', 'default': 'hex', |
| 63 | + 'values': ('ascii', 'dec', 'hex', 'oct', 'bin')}, |
| 64 | + {'id': 'print_sec', 'desc': 'Print start time (sec)', 'default': 'no', 'values': ('yes', 'no')}, |
| 65 | + ) |
| 66 | + annotations = ( |
| 67 | + ('read-packet', 'READ'), |
| 68 | + ('write-packet', 'WRITE'), |
| 69 | + ) |
| 70 | + annotation_rows = ( |
| 71 | + ('packet_read', 'RD', (Ann.PACKET_READ, )), |
| 72 | + ('packet_write', 'WR', (Ann.PACKET_WRITE, )), |
| 73 | + ) |
| 74 | + |
| 75 | + def __init__(self): |
| 76 | + self.out_py = None |
| 77 | + self.out_ann = None |
| 78 | + self.packet_data = deque() |
| 79 | + self.packet_ss = 0 |
| 80 | + self.packet_es = 0 |
| 81 | + self.read_sign = False |
| 82 | + self.address = 0 |
| 83 | + self.print_sec = False |
| 84 | + self.fmt = None |
| 85 | + self.samplerate = 0 |
| 86 | + self.sampletime = 0 |
| 87 | + self.reset() |
| 88 | + |
| 89 | + def metadata(self, key, value): |
| 90 | + if key == srd.SRD_CONF_SAMPLERATE: |
| 91 | + self.samplerate = value |
| 92 | + self.sampletime = 1.0 / self.samplerate |
| 93 | + |
| 94 | + def start(self): |
| 95 | + self.out_ann = self.register(srd.OUTPUT_ANN) |
| 96 | + self.out_py = self.register(srd.OUTPUT_PYTHON) |
| 97 | + |
| 98 | + format_name = self.options['format'] |
| 99 | + if format_name == 'hex': |
| 100 | + self.fmt = '{:02X}' |
| 101 | + elif format_name == 'dec': |
| 102 | + self.fmt = '{:d}' |
| 103 | + elif format_name == 'bin': |
| 104 | + self.fmt = '{:08b}' |
| 105 | + elif format_name == 'oct': |
| 106 | + self.fmt = '{:03o}' |
| 107 | + else: |
| 108 | + self.fmt = None |
| 109 | + |
| 110 | + self.print_sec = self.options['print_sec'] == 'yes' |
| 111 | + self.packet_data = deque() |
| 112 | + |
| 113 | + def reset(self): |
| 114 | + self.packet_data.clear() |
| 115 | + self.packet_ss = 0 |
| 116 | + self.packet_es = 0 |
| 117 | + self.address = 0 |
| 118 | + |
| 119 | + def putg(self, ss, es, data): |
| 120 | + """Put a graphical annotation.""" |
| 121 | + self.put(ss, es, self.out_ann, data) |
| 122 | + |
| 123 | + def putp(self, ss, es, data): |
| 124 | + """Put a python annotation.""" |
| 125 | + self.put(ss, es, self.out_py, data) |
| 126 | + |
| 127 | + def format_data_value(self, v): |
| 128 | + # Assume "is printable" for values from 32 to including 126, |
| 129 | + # below 32 is "control" and thus not printable, above 127 is |
| 130 | + # "not ASCII" in its strict sense, 127 (DEL) is not printable, |
| 131 | + # fall back to hex representation for non-printables. |
| 132 | + if self.fmt is None: |
| 133 | + if 32 <= v <= 126: |
| 134 | + return chr(v) |
| 135 | + return "[{:02X}]".format(v) |
| 136 | + else: |
| 137 | + return self.fmt.format(v) |
| 138 | + |
| 139 | + def data_array_to_str(self, data_array): |
| 140 | + if self.fmt: |
| 141 | + str_array = [self.fmt.format(value) for value in data_array] |
| 142 | + return ' '.join(str_array) |
| 143 | + else: |
| 144 | + str_array = [self.format_data_value(value) for value in data_array] |
| 145 | + return ''.join(str_array) |
| 146 | + |
| 147 | + def handle_packet(self): |
| 148 | + if len(self.packet_data): |
| 149 | + packet_str = self.data_array_to_str(self.packet_data) |
| 150 | + if self.read_sign: |
| 151 | + ann = Ann.PACKET_READ |
| 152 | + else: |
| 153 | + ann = Ann.PACKET_WRITE |
| 154 | + |
| 155 | + packet_str = "ADDR 0x{:02X} {:}: ".format( |
| 156 | + self.address, |
| 157 | + 'RD' if self.read_sign else 'WR', |
| 158 | + ) + packet_str |
| 159 | + |
| 160 | + if self.print_sec and self.sampletime: |
| 161 | + packet_str = "{:8.3f} ".format(self.packet_ss * self.sampletime,) + packet_str |
| 162 | + |
| 163 | + self.putg(self.packet_ss, self.packet_es, [ann, |
| 164 | + [ |
| 165 | + packet_str, |
| 166 | + packet_str[7+9:] if self.print_sec else packet_str[7:], |
| 167 | + ]]) |
| 168 | + ptype = 'PACKET READ' if self.read_sign else 'PACKET WRITE' |
| 169 | + self.putp(self.packet_ss, self.packet_es, (ptype, (self.address, tuple(self.packet_data)))) |
| 170 | + self.reset() |
| 171 | + |
| 172 | + def decode(self, ss, es, data): |
| 173 | + ptype = data[0] |
| 174 | + if 'DATA' in ptype: |
| 175 | + self.packet_data.append(data[1]) |
| 176 | + self.packet_es = es |
| 177 | + elif ptype.startswith('START'): |
| 178 | + if 'REPEAT' in ptype: |
| 179 | + self.packet_es = es |
| 180 | + self.handle_packet() |
| 181 | + self.packet_ss = ss |
| 182 | + elif 'ADDRESS' in ptype: |
| 183 | + self.address = data[1] |
| 184 | + self.read_sign = 'READ' in ptype |
| 185 | + self.packet_es = es |
| 186 | + elif ptype.endswith('ACK'): |
| 187 | + self.packet_es = es |
| 188 | + elif ptype == 'STOP': |
| 189 | + self.packet_es = es |
| 190 | + self.handle_packet() |
0 commit comments