]>
Commit | Line | Data |
---|---|---|
93b92702 GS |
1 | ## |
2 | ## This file is part of the libsigrokdecode project. | |
3 | ## | |
4 | ## Copyright (C) 2017 Gerhard Sittig <gerhard.sittig@gmx.net> | |
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 | ||
20 | # This implementation is incomplete. TODO items: | |
21 | # - Support the optional RESET# pin, detect cold and warm reset. | |
22 | # - Split slot values into audio samples of their respective width and | |
23 | # frequency (either on user provided parameters, or from inspection of | |
24 | # decoded register access). | |
25 | ||
26 | import sigrokdecode as srd | |
a83e259a | 27 | from common.srdhelper import SrdIntEnum |
93b92702 GS |
28 | |
29 | class ChannelError(Exception): | |
30 | pass | |
31 | ||
29cc1e22 | 32 | Pin = SrdIntEnum.from_str('Pin', 'SYNC BIT_CLK SDATA_OUT SDATA_IN RESET') |
a83e259a UH |
33 | |
34 | slots = 'TAG ADDR DATA 03 04 05 06 07 08 09 10 11 IO'.split() | |
35 | a = 'BITS_OUT BITS_IN SLOT_RAW_OUT SLOT_RAW_IN WARN ERROR'.split() + \ | |
36 | ['SLOT_OUT_' + s for s in slots] + ['SLOT_IN_' + s for s in slots] | |
37 | Ann = SrdIntEnum.from_list('Ann', a) | |
38 | ||
39 | Bin = SrdIntEnum.from_str('Bin', 'FRAME_OUT FRAME_IN SLOT_RAW_OUT SLOT_RAW_IN') | |
93b92702 GS |
40 | |
41 | class Decoder(srd.Decoder): | |
42 | api_version = 3 | |
43 | id = 'ac97' | |
44 | name = "AC '97" | |
45 | longname = "Audio Codec '97" | |
46 | desc = 'Audio and modem control for PC systems.' | |
47 | license = 'gplv2+' | |
48 | inputs = ['logic'] | |
6cbba91f | 49 | outputs = [] |
d6d8a8a4 | 50 | tags = ['Audio', 'PC'] |
93b92702 GS |
51 | channels = ( |
52 | {'id': 'sync', 'name': 'SYNC', 'desc': 'Frame synchronization'}, | |
53 | {'id': 'clk', 'name': 'BIT_CLK', 'desc': 'Data bits clock'}, | |
54 | ) | |
55 | optional_channels = ( | |
56 | {'id': 'out', 'name': 'SDATA_OUT', 'desc': 'Data output'}, | |
57 | {'id': 'in', 'name': 'SDATA_IN', 'desc': 'Data input'}, | |
58 | {'id': 'rst', 'name': 'RESET#', 'desc': 'Reset line'}, | |
59 | ) | |
60 | annotations = ( | |
e144452b UH |
61 | ('bit-out', 'Output bit'), |
62 | ('bit-in', 'Input bit'), | |
93b92702 | 63 | ('slot-out-raw', 'Output raw value'), |
a83e259a UH |
64 | ('slot-in-raw', 'Input raw value'), |
65 | ('warning', 'Warning'), | |
66 | ('error', 'Error'), | |
93b92702 GS |
67 | ('slot-out-tag', 'Output TAG'), |
68 | ('slot-out-cmd-addr', 'Output command address'), | |
69 | ('slot-out-cmd-data', 'Output command data'), | |
70 | ('slot-out-03', 'Output slot 3'), | |
71 | ('slot-out-04', 'Output slot 4'), | |
72 | ('slot-out-05', 'Output slot 5'), | |
73 | ('slot-out-06', 'Output slot 6'), | |
74 | ('slot-out-07', 'Output slot 7'), | |
75 | ('slot-out-08', 'Output slot 8'), | |
76 | ('slot-out-09', 'Output slot 9'), | |
77 | ('slot-out-10', 'Output slot 10'), | |
78 | ('slot-out-11', 'Output slot 11'), | |
79 | ('slot-out-io-ctrl', 'Output I/O control'), | |
93b92702 GS |
80 | ('slot-in-tag', 'Input TAG'), |
81 | ('slot-in-sts-addr', 'Input status address'), | |
82 | ('slot-in-sts-data', 'Input status data'), | |
83 | ('slot-in-03', 'Input slot 3'), | |
84 | ('slot-in-04', 'Input slot 4'), | |
85 | ('slot-in-05', 'Input slot 5'), | |
86 | ('slot-in-06', 'Input slot 6'), | |
87 | ('slot-in-07', 'Input slot 7'), | |
88 | ('slot-in-08', 'Input slot 8'), | |
89 | ('slot-in-09', 'Input slot 9'), | |
90 | ('slot-in-10', 'Input slot 10'), | |
91 | ('slot-in-11', 'Input slot 11'), | |
92 | ('slot-in-io-sts', 'Input I/O status'), | |
93 | # TODO: Add more annotation classes: | |
94 | # TAG: 'ready', 'valid', 'id', 'rsv' | |
95 | # CMD ADDR: 'r/w', 'addr', 'unused' | |
96 | # CMD DATA: 'data', 'unused' | |
97 | # 3-11: 'data', 'unused', 'double data' | |
93b92702 GS |
98 | ) |
99 | annotation_rows = ( | |
100 | ('bits-out', 'Output bits', (Ann.BITS_OUT,)), | |
a83e259a UH |
101 | ('slots-out-raw', 'Output numbers', (Ann.SLOT_RAW_OUT,)), |
102 | ('slots-out', 'Output slots', Ann.prefixes('SLOT_OUT_')), | |
93b92702 | 103 | ('bits-in', 'Input bits', (Ann.BITS_IN,)), |
a83e259a UH |
104 | ('slots-in-raw', 'Input numbers', (Ann.SLOT_RAW_IN,)), |
105 | ('slots-in', 'Input slots', Ann.prefixes('SLOT_IN_')), | |
93b92702 GS |
106 | ('warnings', 'Warnings', (Ann.WARN,)), |
107 | ('errors', 'Errors', (Ann.ERROR,)), | |
108 | ) | |
109 | binary = ( | |
110 | ('frame-out', 'Frame bits, output data'), | |
111 | ('frame-in', 'Frame bits, input data'), | |
112 | ('slot-raw-out', 'Raw slot bits, output data'), | |
113 | ('slot-raw-in', 'Raw slot bits, input data'), | |
114 | # TODO: Which (other) binary classes to implement? | |
115 | # - Are binary annotations per audio slot useful? | |
116 | # - Assume 20bit per slot, in 24bit units? Or assume 16bit | |
117 | # audio samples? Observe register access and derive width | |
118 | # of the audio data? Dump channels 3-11 or 1-12? | |
119 | ) | |
120 | ||
121 | def putx(self, ss, es, cls, data): | |
122 | self.put(ss, es, self.out_ann, [cls, data]) | |
123 | ||
124 | def putf(self, frombit, bitcount, cls, data): | |
125 | ss = self.frame_ss_list[frombit] | |
126 | es = self.frame_ss_list[frombit + bitcount] | |
127 | self.putx(ss, es, cls, data) | |
128 | ||
129 | def putb(self, frombit, bitcount, cls, data): | |
130 | ss = self.frame_ss_list[frombit] | |
131 | es = self.frame_ss_list[frombit + bitcount] | |
132 | self.put(ss, es, self.out_binary, [cls, data]) | |
133 | ||
134 | def __init__(self): | |
93b92702 GS |
135 | self.reset() |
136 | ||
137 | def reset(self): | |
138 | self.frame_ss_list = None | |
139 | self.frame_slot_lens = [0, 16] + [16 + 20 * i for i in range(1, 13)] | |
140 | self.frame_total_bits = self.frame_slot_lens[-1] | |
141 | self.handle_slots = { | |
142 | 0: self.handle_slot_00, | |
143 | 1: self.handle_slot_01, | |
144 | 2: self.handle_slot_02, | |
145 | } | |
146 | ||
147 | def start(self): | |
1d7e79da UH |
148 | self.out_binary = self.register(srd.OUTPUT_BINARY) |
149 | self.out_ann = self.register(srd.OUTPUT_ANN) | |
93b92702 GS |
150 | |
151 | def metadata(self, key, value): | |
152 | if key == srd.SRD_CONF_SAMPLERATE: | |
153 | self.samplerate = value | |
154 | ||
155 | def bits_to_int(self, bits): | |
156 | # Convert MSB-first bit sequence to integer value. | |
157 | if not bits: | |
158 | return 0 | |
159 | count = len(bits) | |
160 | value = sum([2 ** (count - 1 - i) for i in range(count) if bits[i]]) | |
161 | return value | |
162 | ||
163 | def bits_to_bin_ann(self, bits): | |
164 | # Convert MSB-first bit sequence to binary annotation data. | |
165 | # It's assumed that the number of bits does not (in useful ways) | |
166 | # fit into an integer, and we need to create an array of bytes | |
167 | # from the data afterwards, anyway. Hence the separate routine | |
168 | # and the conversion of eight bits each. | |
169 | out = [] | |
170 | count = len(bits) | |
171 | while count > 0: | |
172 | count -= 8 | |
173 | by, bits = bits[:8], bits[8:] | |
174 | by = self.bits_to_int(by) | |
175 | out.append(by) | |
176 | out = bytes(out) | |
177 | return out | |
178 | ||
179 | def int_to_nibble_text(self, value, bitcount): | |
180 | # Convert number to hex digits for given bit count. | |
181 | digits = (bitcount + 3) // 4 | |
182 | text = '{{:0{:d}x}}'.format(digits).format(value) | |
183 | return text | |
184 | ||
185 | def get_bit_field(self, data, size, off, count): | |
186 | shift = size - off - count | |
187 | data >>= shift | |
188 | mask = (1 << count) - 1 | |
189 | data &= mask | |
190 | return data | |
191 | ||
192 | def flush_frame_bits(self): | |
193 | # Flush raw frame bits to binary annotation. | |
93b92702 GS |
194 | data = self.frame_bits_out[:] |
195 | count = len(data) | |
196 | data = self.bits_to_bin_ann(data) | |
a83e259a | 197 | self.putb(0, count, Bin.FRAME_OUT, data) |
93b92702 | 198 | |
93b92702 GS |
199 | data = self.frame_bits_in[:] |
200 | count = len(data) | |
201 | data = self.bits_to_bin_ann(data) | |
a83e259a | 202 | self.putb(0, count, Bin.FRAME_IN, data) |
93b92702 GS |
203 | |
204 | def start_frame(self, ss): | |
205 | # Mark the start of a frame. | |
206 | if self.frame_ss_list: | |
207 | # Flush bits if we had a frame before the frame which is | |
208 | # starting here. | |
209 | self.flush_frame_bits() | |
210 | self.frame_ss_list = [ss] | |
211 | self.frame_bits_out = [] | |
212 | self.frame_bits_in = [] | |
213 | self.frame_slot_data_out = [] | |
214 | self.frame_slot_data_in = [] | |
215 | self.have_slots = {True: None, False: None} | |
216 | ||
217 | def handle_slot_dummy(self, slotidx, bitidx, bitcount, is_out, data): | |
218 | # Handle slot x, default/fallback handler. | |
219 | # Only process data of slots 1-12 when slot 0 says "valid". | |
220 | if not self.have_slots[is_out]: | |
221 | return | |
222 | if not self.have_slots[is_out][slotidx]: | |
223 | return | |
224 | ||
225 | # Emit a naive annotation with just the data bits that we saw | |
226 | # for the slot (hex nibbles for density). For audio data this | |
227 | # can be good enough. Slots with special meaning should not end | |
228 | # up calling the dummy handler. | |
229 | text = self.int_to_nibble_text(data, bitcount) | |
230 | anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG | |
231 | self.putf(bitidx, bitcount, anncls + slotidx, [text]) | |
232 | ||
233 | # Emit binary output for the data that is contained in slots | |
234 | # which end up calling the default handler. This transparently | |
235 | # should translate to "the slots with audio data", as other | |
236 | # slots which contain management data should have their specific | |
237 | # handler routines. In the present form, this approach might be | |
238 | # good enough to get a (header-less) audio stream for typical | |
239 | # setups where only line-in or line-out are in use. | |
240 | # | |
241 | # TODO: Improve this early prototype implementation. For now the | |
242 | # decoder just exports the upper 16 bits of each audio channel | |
243 | # that happens to be valid. For an improved implementation, it | |
244 | # either takes user provided specs or more smarts like observing | |
245 | # register access (if the capture includes it). | |
a83e259a | 246 | anncls = Bin.SLOT_RAW_OUT if is_out else Bin.SLOT_RAW_IN |
93b92702 GS |
247 | data_bin = data >> 4 |
248 | data_bin &= 0xffff | |
249 | data_bin = data_bin.to_bytes(2, byteorder = 'big') | |
250 | self.putb(bitidx, bitcount, anncls, data_bin) | |
251 | ||
252 | def handle_slot_00(self, slotidx, bitidx, bitcount, is_out, data): | |
253 | # Handle slot 0, TAG. | |
254 | slotpos = self.frame_slot_lens[slotidx] | |
255 | fieldoff = 0 | |
256 | anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG | |
257 | ||
258 | fieldlen = 1 | |
259 | ready = self.get_bit_field(data, bitcount, fieldoff, fieldlen) | |
260 | text = ['READY: 1', 'READY', 'RDY', 'R'] if ready else ['ready: 0', 'rdy', '-'] | |
261 | self.putf(slotpos + fieldoff, fieldlen, anncls, text) | |
262 | fieldoff += fieldlen | |
263 | ||
264 | fieldlen = 12 | |
265 | valid = self.get_bit_field(data, bitcount, fieldoff, fieldlen) | |
266 | text = ['VALID: {:3x}'.format(valid), '{:3x}'.format(valid)] | |
267 | self.putf(slotpos + fieldoff, fieldlen, anncls, text) | |
268 | have_slots = [True] + [False] * 12 | |
269 | for idx in range(12): | |
270 | have_slots[idx + 1] = bool(valid & (1 << (11 - idx))) | |
271 | self.have_slots[is_out] = have_slots | |
272 | fieldoff += fieldlen | |
273 | ||
274 | fieldlen = 1 | |
275 | rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen) | |
276 | if rsv != 0: | |
277 | text = ['reserved bit error', 'rsv error', 'rsv'] | |
278 | self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text) | |
279 | fieldoff += fieldlen | |
280 | ||
281 | # TODO: Will input slot 0 have a Codec ID, or 3 reserved bits? | |
282 | fieldlen = 2 | |
283 | codec = self.get_bit_field(data, bitcount, fieldoff, fieldlen) | |
284 | text = ['CODEC: {:1x}'.format(codec), '{:1x}'.format(codec)] | |
285 | self.putf(slotpos + fieldoff, fieldlen, anncls, text) | |
286 | fieldoff += fieldlen | |
287 | ||
288 | def handle_slot_01(self, slotidx, bitidx, bitcount, is_out, data): | |
289 | # Handle slot 1, command/status address. | |
290 | slotpos = self.frame_slot_lens[slotidx] | |
291 | if not self.have_slots[is_out]: | |
292 | return | |
293 | if not self.have_slots[is_out][slotidx]: | |
294 | return | |
295 | fieldoff = 0 | |
296 | anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG | |
297 | anncls += slotidx | |
298 | ||
299 | fieldlen = 1 | |
300 | if is_out: | |
301 | is_read = self.get_bit_field(data, bitcount, fieldoff, fieldlen) | |
302 | text = ['READ', 'RD', 'R'] if is_read else ['WRITE', 'WR', 'W'] | |
303 | self.putf(slotpos + fieldoff, fieldlen, anncls, text) | |
304 | # TODO: Check for the "atomic" constraint? Some operations | |
305 | # involve address _and_ data, which cannot be spread across | |
306 | # several frames. Slot 0 and 1 _must_ be provided within the | |
307 | # same frame (the test should occur in the handler for slot | |
308 | # 2 of course, in slot 1 we don't know what will follow). | |
309 | else: | |
310 | rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen) | |
311 | if rsv != 0: | |
312 | text = ['reserved bit error', 'rsv error', 'rsv'] | |
313 | self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text) | |
314 | fieldoff += fieldlen | |
315 | ||
316 | fieldlen = 7 | |
317 | regaddr = self.get_bit_field(data, bitcount, fieldoff, fieldlen) | |
318 | # TODO: Present 0-63 or 0-126 as the address of the 16bit register? | |
319 | text = ['ADDR: {:2x}'.format(regaddr), '{:2x}'.format(regaddr)] | |
320 | self.putf(slotpos + fieldoff, fieldlen, anncls, text) | |
321 | if regaddr & 0x01: | |
322 | text = ['odd register address', 'odd reg addr', 'odd addr', 'odd'] | |
323 | self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text) | |
324 | fieldoff += fieldlen | |
325 | ||
326 | # Strictly speaking there are 10 data request bits and 2 reserved | |
327 | # bits for input slots, and 12 reserved bits for output slots. We | |
328 | # test for 10 and 2 bits, to simplify the logic. Only in case of | |
329 | # non-zero reserved bits for outputs this will result in "a little | |
330 | # strange" an annotation. This is a cosmetic issue, we don't mind. | |
331 | fieldlen = 10 | |
332 | reqdata = self.get_bit_field(data, bitcount, fieldoff, fieldlen) | |
333 | if is_out and reqdata != 0: | |
334 | text = ['reserved bit error', 'rsv error', 'rsv'] | |
335 | self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text) | |
336 | if not is_out: | |
337 | text = ['REQ: {:3x}'.format(reqdata), '{:3x}'.format(reqdata)] | |
338 | self.putf(slotpos + fieldoff, fieldlen, anncls, text) | |
339 | fieldoff += fieldlen | |
340 | ||
341 | fieldlen = 2 | |
342 | rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen) | |
343 | if rsv != 0: | |
344 | text = ['reserved bit error', 'rsv error', 'rsv'] | |
345 | self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text) | |
346 | fieldoff += fieldlen | |
347 | ||
348 | def handle_slot_02(self, slotidx, bitidx, bitcount, is_out, data): | |
349 | # Handle slot 2, command/status data. | |
350 | slotpos = self.frame_slot_lens[slotidx] | |
351 | if not self.have_slots[is_out]: | |
352 | return | |
353 | if not self.have_slots[is_out][slotidx]: | |
354 | return | |
355 | fieldoff = 0 | |
356 | anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG | |
357 | anncls += slotidx | |
358 | ||
359 | fieldlen = 16 | |
360 | rwdata = self.get_bit_field(data, bitcount, fieldoff, fieldlen) | |
361 | # TODO: Check for zero output data when the operation is a read. | |
362 | # TODO: Check for the "atomic" constraint. | |
363 | text = ['DATA: {:4x}'.format(rwdata), '{:4x}'.format(rwdata)] | |
364 | self.putf(slotpos + fieldoff, fieldlen, anncls, text) | |
365 | fieldoff += fieldlen | |
366 | ||
367 | fieldlen = 4 | |
368 | rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen) | |
369 | if rsv != 0: | |
370 | text = ['reserved bits error', 'rsv error', 'rsv'] | |
371 | self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text) | |
372 | fieldoff += fieldlen | |
373 | ||
374 | # TODO: Implement other slots. | |
375 | # - 1: cmd/status addr (check status vs command) | |
376 | # - 2: cmd/status data (check status vs command) | |
377 | # - 3-11: audio out/in | |
378 | # - 12: io control/status (modem GPIO(?)) | |
379 | ||
380 | def handle_slot(self, slotidx, data_out, data_in): | |
381 | # Process a received slot of a frame. | |
382 | func = self.handle_slots.get(slotidx, self.handle_slot_dummy) | |
383 | bitidx = self.frame_slot_lens[slotidx] | |
384 | bitcount = self.frame_slot_lens[slotidx + 1] - bitidx | |
385 | if data_out is not None: | |
386 | func(slotidx, bitidx, bitcount, True, data_out) | |
387 | if data_in is not None: | |
388 | func(slotidx, bitidx, bitcount, False, data_in) | |
389 | ||
390 | def handle_bits(self, ss, es, bit_out, bit_in): | |
391 | # Process a received pair of bits. | |
392 | # Emit the bits' annotations. Only interpret the data when we | |
393 | # are in a frame (have seen the start of the frame, and don't | |
394 | # exceed the expected number of bits in a frame). | |
395 | if bit_out is not None: | |
396 | self.putx(ss, es, Ann.BITS_OUT, ['{:d}'.format(bit_out)]) | |
397 | if bit_in is not None: | |
398 | self.putx(ss, es, Ann.BITS_IN, ['{:d}'.format(bit_in)]) | |
399 | if self.frame_ss_list is None: | |
400 | return | |
401 | self.frame_ss_list.append(es) | |
402 | have_len = len(self.frame_ss_list) - 1 | |
403 | if have_len > self.frame_total_bits: | |
404 | return | |
405 | ||
406 | # Accumulate the bits within the frame, until one slot of the | |
407 | # frame has become available. | |
408 | slot_idx = 0 | |
409 | if bit_out is not None: | |
410 | self.frame_bits_out.append(bit_out) | |
411 | slot_idx = len(self.frame_slot_data_out) | |
412 | if bit_in is not None: | |
413 | self.frame_bits_in.append(bit_in) | |
414 | slot_idx = len(self.frame_slot_data_in) | |
415 | want_len = self.frame_slot_lens[slot_idx + 1] | |
416 | if have_len != want_len: | |
417 | return | |
418 | prev_len = self.frame_slot_lens[slot_idx] | |
419 | ||
420 | # Convert bits to integer values. This shall simplify extraction | |
421 | # of bit fields in multiple other locations. | |
422 | slot_data_out = None | |
423 | if bit_out is not None: | |
424 | slot_bits = self.frame_bits_out[prev_len:] | |
425 | slot_data = self.bits_to_int(slot_bits) | |
426 | self.frame_slot_data_out.append(slot_data) | |
427 | slot_data_out = slot_data | |
428 | slot_data_in = None | |
429 | if bit_in is not None: | |
430 | slot_bits = self.frame_bits_in[prev_len:] | |
431 | slot_data = self.bits_to_int(slot_bits) | |
432 | self.frame_slot_data_in.append(slot_data) | |
433 | slot_data_in = slot_data | |
434 | ||
435 | # Emit simple annotations for the integer values, until upper | |
436 | # layer decode stages will be implemented. | |
437 | slot_len = have_len - prev_len | |
438 | slot_ss = self.frame_ss_list[prev_len] | |
439 | slot_es = self.frame_ss_list[have_len] | |
440 | if slot_data_out is not None: | |
441 | slot_text = self.int_to_nibble_text(slot_data_out, slot_len) | |
a83e259a | 442 | self.putx(slot_ss, slot_es, Ann.SLOT_RAW_OUT, [slot_text]) |
93b92702 GS |
443 | if slot_data_in is not None: |
444 | slot_text = self.int_to_nibble_text(slot_data_in, slot_len) | |
a83e259a | 445 | self.putx(slot_ss, slot_es, Ann.SLOT_RAW_IN, [slot_text]) |
93b92702 GS |
446 | |
447 | self.handle_slot(slot_idx, slot_data_out, slot_data_in) | |
448 | ||
449 | def decode(self): | |
29cc1e22 UH |
450 | have_sdo = self.has_channel(Pin.SDATA_OUT) |
451 | have_sdi = self.has_channel(Pin.SDATA_IN) | |
93b92702 GS |
452 | if not have_sdo and not have_sdi: |
453 | raise ChannelError('Either SDATA_OUT or SDATA_IN (or both) are required.') | |
29cc1e22 | 454 | have_reset = self.has_channel(Pin.RESET) |
93b92702 GS |
455 | |
456 | # Data is sampled at falling CLK edges. Annotations need to span | |
457 | # the period between rising edges. SYNC rises one cycle _before_ | |
458 | # the start of a frame. Grab the earliest SYNC sample we can get | |
459 | # and advance to the start of a bit time. Then keep getting the | |
460 | # samples and the end of all subsequent bit times. | |
461 | prev_sync = [None, None, None] | |
29cc1e22 UH |
462 | pins = self.wait({Pin.BIT_CLK: 'e'}) |
463 | if pins[Pin.BIT_CLK] == 0: | |
464 | prev_sync[-1] = pins[Pin.SYNC] | |
465 | pins = self.wait({Pin.BIT_CLK: 'r'}) | |
93b92702 GS |
466 | bit_ss = self.samplenum |
467 | while True: | |
29cc1e22 | 468 | pins = self.wait({Pin.BIT_CLK: 'f'}) |
93b92702 | 469 | prev_sync.pop(0) |
29cc1e22 UH |
470 | prev_sync.append(pins[Pin.SYNC]) |
471 | self.wait({Pin.BIT_CLK: 'r'}) | |
93b92702 GS |
472 | if prev_sync[0] == 0 and prev_sync[1] == 1: |
473 | self.start_frame(bit_ss) | |
474 | self.handle_bits(bit_ss, self.samplenum, | |
29cc1e22 UH |
475 | pins[Pin.SDATA_OUT] if have_sdo else None, |
476 | pins[Pin.SDATA_IN] if have_sdi else None) | |
93b92702 | 477 | bit_ss = self.samplenum |