Skip to content

Commit 9aa2d31

Browse files
committed
i2c_packet: decoder for forming packets from i2c data
i2c_packet protocol decoder stacks with i2c PD and allows to combine several data bytes from "START" i2c condition until "STOP" and puts this data in a dedicated annotation. Possible data formats for data output are hex, ascii, dec, bin and oct.
1 parent e556e11 commit 9aa2d31

2 files changed

Lines changed: 226 additions & 0 deletions

File tree

decoders/i2c_packet/__init__.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
'''
25+
Make data packets from I²C decoder data.
26+
'''
27+
28+
from .pd import Decoder

decoders/i2c_packet/pd.py

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
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+
- 'TRANSACTION END' (End of transaction, due toi STOP bus condition)
39+
40+
<pdata> is the tuple with slave address byte and tuple with data bytes
41+
if ptype is 'PACKET READ' or 'PACKET WRITE'.
42+
Slave addresses do not include bit 0 (the READ/WRITE indication bit).
43+
<pdata is None if ptype is 'TRANSACTION END'.
44+
'''
45+
46+
class Ann:
47+
DATA = 0
48+
49+
class Decoder(srd.Decoder):
50+
api_version = 3
51+
id = 'i2c_packet'
52+
name = 'I²C packet'
53+
longname = 'I²C packet builder'
54+
desc = 'Concatenate I²C data to packets'
55+
license = 'mit'
56+
inputs = ['i2c']
57+
outputs = []
58+
tags = ['Embedded/industrial']
59+
options = (
60+
{'id': 'format', 'desc': 'Data format', 'default': 'hex',
61+
'values': ('ascii', 'dec', 'hex', 'oct', 'bin')},
62+
)
63+
annotations = (
64+
('data', 'Data'),
65+
)
66+
annotation_rows = (
67+
('packet', 'Packet', (Ann.DATA,)),
68+
)
69+
70+
def __init__(self):
71+
self.out_py = None
72+
self.out_ann = None
73+
self.packet_data = deque()
74+
self.packet_str = ''
75+
self.packet_str_short = ''
76+
self.packet_ss = 0
77+
self.packet_part_ss = 0
78+
self.packet_es = 0
79+
self.read_sign = False
80+
self.address = 0
81+
self.fmt = None
82+
self.reset()
83+
84+
def start(self):
85+
self.out_ann = self.register(srd.OUTPUT_ANN)
86+
self.out_py = self.register(srd.OUTPUT_PYTHON)
87+
88+
format_name = self.options['format']
89+
if format_name == 'hex':
90+
self.fmt = '{:02X}'
91+
elif format_name == 'dec':
92+
self.fmt = '{:d}'
93+
elif format_name == 'bin':
94+
self.fmt = '{:08b}'
95+
elif format_name == 'oct':
96+
self.fmt = '{:03o}'
97+
else:
98+
self.fmt = None
99+
self.packet_data = deque()
100+
101+
def reset(self):
102+
self.packet_data.clear()
103+
self.packet_str = ''
104+
self.packet_str_short = ''
105+
self.packet_ss = 0
106+
self.packet_part_ss = 0
107+
self.packet_es = 0
108+
self.address = 0
109+
110+
def putg(self, ss, es, data):
111+
"""Put a graphical annotation."""
112+
self.put(ss, es, self.out_ann, data)
113+
114+
def putp(self, ss, es, data):
115+
"""Put a python annotation."""
116+
self.put(ss, es, self.out_py, data)
117+
118+
def format_data_value(self, v):
119+
# Assume "is printable" for values from 32 to including 126,
120+
# below 32 is "control" and thus not printable, above 127 is
121+
# "not ASCII" in its strict sense, 127 (DEL) is not printable,
122+
# fall back to hex representation for non-printables.
123+
# (comment from same code of uart PD by Gerhard Sittig @gsigh)
124+
if self.fmt is None:
125+
if 32 <= v <= 126:
126+
return chr(v)
127+
return "[{:02X}]".format(v)
128+
else:
129+
return self.fmt.format(v)
130+
131+
def data_array_to_str(self, data_array):
132+
if self.fmt:
133+
str_array = [self.fmt.format(value) for value in data_array]
134+
return ' '.join(str_array)
135+
else:
136+
str_array = [self.format_data_value(value) for value in data_array]
137+
return ''.join(str_array)
138+
139+
def format_packet(self):
140+
packet_str = "0x{:02X} {:}: ".format(
141+
self.address,
142+
'RD' if self.read_sign else 'WR',
143+
) + self.data_array_to_str(self.packet_data)
144+
145+
packet_str_short = packet_str[2:]
146+
147+
if self.packet_str:
148+
packet_str = self.packet_str + ' [SR] ' + packet_str
149+
packet_str_short = self.packet_str_short + ' [SR] ' + packet_str_short
150+
151+
return packet_str, packet_str_short
152+
153+
def handle_packet(self, start_repeat=False):
154+
if not len(self.packet_data):
155+
if not start_repeat:
156+
self.reset()
157+
return
158+
159+
packet_str, packet_str_short = self.format_packet()
160+
161+
ptype = 'PACKET READ' if self.read_sign else 'PACKET WRITE'
162+
self.putp(self.packet_part_ss, self.packet_es,
163+
(ptype, (self.address, tuple(self.packet_data))))
164+
if not start_repeat:
165+
self.putp(self.packet_es, self.packet_es,
166+
('TRANSACTION END', None))
167+
168+
if start_repeat:
169+
self.packet_data.clear()
170+
self.packet_str = packet_str
171+
self.packet_str_short = packet_str_short
172+
else:
173+
packet_ss = self.packet_ss
174+
self.putg(packet_ss, self.packet_es,
175+
[Ann.DATA, [packet_str, packet_str_short]])
176+
self.reset()
177+
178+
def decode(self, ss, es, data):
179+
ptype = data[0]
180+
if ptype.startswith('DATA'):
181+
self.packet_data.append(data[1])
182+
self.packet_es = es
183+
elif ptype.startswith('START'):
184+
# If there is still data in the reception buffer, put the packet annotation
185+
start_repeat = 'REPEAT' in ptype
186+
self.handle_packet(start_repeat=start_repeat)
187+
self.packet_part_ss = ss
188+
if not start_repeat:
189+
self.packet_ss = ss
190+
elif ptype.startswith('ADDRESS'):
191+
self.address = data[1]
192+
self.read_sign = 'READ' in ptype
193+
self.packet_es = es
194+
elif ptype.endswith('ACK'):
195+
self.packet_es = es
196+
elif ptype == 'STOP':
197+
self.packet_es = es
198+
self.handle_packet()

0 commit comments

Comments
 (0)