]> sigrok.org Git - libsigrokdecode.git/blame - decoders/cec/pd.py
avr_isp: Add more parts
[libsigrokdecode.git] / decoders / cec / pd.py
CommitLineData
fb248c04
JS
1##
2## This file is part of the libsigrokdecode project.
3##
4## Copyright (C) 2018 Jorge Solla Rubiales <jorgesolla@gmail.com>
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
20import sigrokdecode as srd
21from .protocoldata import *
22
23# Pulse types
24class Pulse:
25 INVALID, START, ZERO, ONE = range(4)
26
27# Protocol stats
28class Stat:
29 WAIT_START, GET_BITS, WAIT_EOM, WAIT_ACK = range(4)
30
31# Pulse times in milliseconds
32timing = {
33 Pulse.START: {
34 'low': { 'min': 3.5, 'max': 3.9 },
35 'total': { 'min': 4.3, 'max': 4.7 }
36 },
37 Pulse.ZERO: {
38 'low': { 'min': 1.3, 'max': 1.7 },
39 'total': { 'min': 2.05, 'max': 2.75 }
40 },
41 Pulse.ONE: {
42 'low': { 'min': 0.4, 'max': 0.8 },
43 'total': { 'min': 2.05, 'max': 2.75 }
44 }
45}
46
47class ChannelError(Exception):
48 pass
49
50class Decoder(srd.Decoder):
51 api_version = 3
52 id = 'cec'
53 name = 'CEC'
54 longname = 'HDMI-CEC'
55 desc = 'HDMI Consumer Electronics Control (CEC) protocol.'
56 license = 'gplv2+'
57 inputs = ['logic']
6cbba91f 58 outputs = []
d6d8a8a4 59 tags = ['Display', 'PC']
fb248c04
JS
60 channels = (
61 {'id': 'cec', 'name': 'CEC', 'desc': 'CEC bus data'},
62 )
63 annotations = (
64 ('st', 'Start'),
65 ('eom-0', 'End of message'),
66 ('eom-1', 'Message continued'),
67 ('nack', 'ACK not set'),
68 ('ack', 'ACK set'),
e144452b
UH
69 ('bit', 'Bit'),
70 ('byte', 'Byte'),
71 ('frame', 'Frame'),
72 ('section', 'Section'),
73 ('warning', 'Warning')
fb248c04
JS
74 )
75 annotation_rows = (
76 ('bits', 'Bits', (0, 1, 2, 3, 4, 5)),
77 ('bytes', 'Bytes', (6,)),
78 ('frames', 'Frames', (7,)),
79 ('sections', 'Sections', (8,)),
80 ('warnings', 'Warnings', (9,))
81 )
82
83 def __init__(self):
84 self.reset()
85
86 def precalculate(self):
87 # Restrict max length of ACK/NACK labels to 2 BIT pulses.
8a8b516a 88 bit_time = timing[Pulse.ZERO]['total']['min'] * 2
fb248c04
JS
89 self.max_ack_len_samples = round((bit_time / 1000) * self.samplerate)
90
91 def reset(self):
92 self.stat = Stat.WAIT_START
93 self.samplerate = None
94 self.fall_start = None
95 self.fall_end = None
96 self.rise = None
97 self.reset_frame_vars()
98
99 def reset_frame_vars(self):
100 self.eom = None
101 self.bit_count = 0
102 self.byte_count = 0
103 self.byte = 0
104 self.byte_start = None
105 self.frame_start = None
106 self.frame_end = None
107 self.is_nack = 0
108 self.cmd_bytes = []
109
110 def metadata(self, key, value):
111 if key == srd.SRD_CONF_SAMPLERATE:
112 self.samplerate = value
113 self.precalculate()
114
fb248c04
JS
115 def handle_frame(self, is_nack):
116 if self.fall_start is None or self.fall_end is None:
117 return
118
119 i = 0
16d2031b 120 string = ''
fb248c04 121 while i < len(self.cmd_bytes):
16d2031b 122 string += '{:02x}'.format(self.cmd_bytes[i]['val'])
fb248c04 123 if i != (len(self.cmd_bytes) - 1):
16d2031b 124 string += ':'
fb248c04
JS
125 i += 1
126
16d2031b 127 self.put(self.frame_start, self.frame_end, self.out_ann, [7, [string]])
fb248c04
JS
128
129 i = 0
130 operands = 0
16d2031b 131 string = ''
fb248c04
JS
132 while i < len(self.cmd_bytes):
133 if i == 0: # Parse header
134 (src, dst) = decode_header(self.cmd_bytes[i]['val'])
16d2031b 135 string = 'HDR: ' + src + ', ' + dst
fb248c04 136 elif i == 1: # Parse opcode
16d2031b 137 string += ' | OPC: ' + opcodes.get(self.cmd_bytes[i]['val'], 'Invalid')
fb248c04
JS
138 else: # Parse operands
139 if operands == 0:
16d2031b 140 string += ' | OPS: '
fb248c04 141 operands += 1
16d2031b 142 string += '0x{:02x}'.format(self.cmd_bytes[i]['val'])
fb248c04 143 if i != len(self.cmd_bytes) - 1:
16d2031b 144 string += ', '
fb248c04
JS
145 i += 1
146
147 # Header only commands are PINGS
148 if i == 1:
16d2031b 149 string += ' | OPC: PING' if self.eom else ' | OPC: NONE. Aborted cmd'
fb248c04
JS
150
151 # Add extra information (ack of the command from the destination)
16d2031b 152 string += ' | R: NACK' if is_nack else ' | R: ACK'
fb248c04 153
16d2031b 154 self.put(self.frame_start, self.frame_end, self.out_ann, [8, [string]])
fb248c04
JS
155
156 def process(self):
157 zero_time = ((self.rise - self.fall_start) / self.samplerate) * 1000.0
158 total_time = ((self.fall_end - self.fall_start) / self.samplerate) * 1000.0
159 pulse = Pulse.INVALID
160
161 # VALIDATION: Identify pulse based on length of the low period
162 for key in timing:
163 if zero_time >= timing[key]['low']['min'] and zero_time <= timing[key]['low']['max']:
164 pulse = key
165 break
166
167 # VALIDATION: Invalid pulse
168 if pulse == Pulse.INVALID:
e8c1209d 169 self.stat = Stat.WAIT_START
fb248c04
JS
170 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Invalid pulse: Wrong timing']])
171 return
172
173 # VALIDATION: If waiting for start, discard everything else
174 if self.stat == Stat.WAIT_START and pulse != Pulse.START:
175 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Expected START: BIT found']])
176 return
177
178 # VALIDATION: If waiting for ACK or EOM, only BIT pulses (0/1) are expected
179 if (self.stat == Stat.WAIT_ACK or self.stat == Stat.WAIT_EOM) and pulse == Pulse.START:
180 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Expected BIT: START received)']])
e8c1209d 181 self.stat = Stat.WAIT_START
fb248c04
JS
182
183 # VALIDATION: ACK bit pulse remains high till the next frame (if any): Validate only min time of the low period
184 if self.stat == Stat.WAIT_ACK and pulse != Pulse.START:
185 if total_time < timing[pulse]['total']['min']:
186 pulse = Pulse.INVALID
187 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['ACK pulse below minimun time']])
e8c1209d 188 self.stat = Stat.WAIT_START
fb248c04
JS
189 return
190
191 # VALIDATION / PING FRAME DETECTION: Initiator doesn't sets the EOM = 1 but stops sending when ack doesn't arrive
192 if self.stat == Stat.GET_BITS and pulse == Pulse.START:
193 # Make sure we received a complete byte to consider it a valid ping
194 if self.bit_count == 0:
195 self.handle_frame(self.is_nack)
196 else:
197 self.put(self.frame_start, self.samplenum, self.out_ann, [9, ['ERROR: Incomplete byte received']])
198
199 # Set wait start so we receive next frame
e8c1209d 200 self.stat = Stat.WAIT_START
fb248c04
JS
201
202 # VALIDATION: Check timing of the BIT (0/1) pulse in any other case (not waiting for ACK)
203 if self.stat != Stat.WAIT_ACK and pulse != Pulse.START:
204 if total_time < timing[pulse]['total']['min'] or total_time > timing[pulse]['total']['max']:
205 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Bit pulse exceeds total pulse timespan']])
206 pulse = Pulse.INVALID
e8c1209d 207 self.stat = Stat.WAIT_START
fb248c04
JS
208 return
209
210 if pulse == Pulse.ZERO:
211 bit = 0
212 elif pulse == Pulse.ONE:
213 bit = 1
214
215 # STATE: WAIT START
216 if self.stat == Stat.WAIT_START:
e8c1209d 217 self.stat = Stat.GET_BITS
fb248c04
JS
218 self.reset_frame_vars()
219 self.put(self.fall_start, self.fall_end, self.out_ann, [0, ['ST']])
220
221 # STATE: GET BITS
222 elif self.stat == Stat.GET_BITS:
223 # Reset stats on first bit
224 if self.bit_count == 0:
225 self.byte_start = self.fall_start
226 self.byte = 0
227
228 # If 1st byte of the datagram save its sample num
229 if len(self.cmd_bytes) == 0:
230 self.frame_start = self.fall_start
231
232 self.byte += (bit << (7 - self.bit_count))
233 self.bit_count += 1
234 self.put(self.fall_start, self.fall_end, self.out_ann, [5, [str(bit)]])
235
236 if self.bit_count == 8:
237 self.bit_count = 0
238 self.byte_count += 1
e8c1209d 239 self.stat = Stat.WAIT_EOM
fb248c04
JS
240 self.put(self.byte_start, self.samplenum, self.out_ann, [6, ['0x{:02x}'.format(self.byte)]])
241 self.cmd_bytes.append({'st': self.byte_start, 'ed': self.samplenum, 'val': self.byte})
242
243 # STATE: WAIT EOM
244 elif self.stat == Stat.WAIT_EOM:
245 self.eom = bit
246 self.frame_end = self.fall_end
247
8a8b516a
UH
248 a = [2, ['EOM=Y']] if self.eom else [1, ['EOM=N']]
249 self.put(self.fall_start, self.fall_end, self.out_ann, a)
fb248c04 250
e8c1209d 251 self.stat = Stat.WAIT_ACK
fb248c04
JS
252
253 # STATE: WAIT ACK
254 elif self.stat == Stat.WAIT_ACK:
255 # If a frame with broadcast destination is being sent, the ACK is
256 # inverted: a 0 is considered a NACK, therefore we invert the value
257 # of the bit here, so we match the real meaning of it.
258 if (self.cmd_bytes[0]['val'] & 0x0F) == 0x0F:
259 bit = ~bit & 0x01
260
261 if (self.fall_end - self.fall_start) > self.max_ack_len_samples:
262 ann_end = self.fall_start + self.max_ack_len_samples
263 else:
264 ann_end = self.fall_end
265
266 if bit:
267 # Any NACK detected in the frame is enough to consider the
268 # whole frame NACK'd.
269 self.is_nack = 1
270 self.put(self.fall_start, ann_end, self.out_ann, [3, ['NACK']])
271 else:
272 self.put(self.fall_start, ann_end, self.out_ann, [4, ['ACK']])
273
274 # After ACK bit, wait for new datagram or continue reading current
275 # one based on EOM value.
276 if self.eom or self.is_nack:
e8c1209d 277 self.stat = Stat.WAIT_START
fb248c04
JS
278 self.handle_frame(self.is_nack)
279 else:
e8c1209d 280 self.stat = Stat.GET_BITS
fb248c04
JS
281
282 def start(self):
283 self.out_ann = self.register(srd.OUTPUT_ANN)
284
285 def decode(self):
286 if not self.samplerate:
287 raise SamplerateError('Cannot decode without samplerate.')
288
289 # Wait for first falling edge.
290 self.wait({0: 'f'})
291 self.fall_end = self.samplenum
292
293 while True:
294 self.wait({0: 'r'})
295 self.rise = self.samplenum
296
297 if self.stat == Stat.WAIT_ACK:
298 self.wait([{0: 'f'}, {'skip': self.max_ack_len_samples}])
299 else:
300 self.wait([{0: 'f'}])
301
302 self.fall_start = self.fall_end
303 self.fall_end = self.samplenum
304 self.process()
305
306 # If there was a timeout while waiting for ACK: RESYNC.
307 # Note: This is an expected situation as no new falling edge will
308 # happen until next frame is transmitted.
309 if self.matched == (False, True):
310 self.wait({0: 'f'})
311 self.fall_end = self.samplenum