]> sigrok.org Git - libsigrokdecode.git/blame - decoders/ac97/pd.py
avr_isp: Add more parts
[libsigrokdecode.git] / decoders / ac97 / pd.py
CommitLineData
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
26import sigrokdecode as srd
a83e259a 27from common.srdhelper import SrdIntEnum
93b92702
GS
28
29class ChannelError(Exception):
30 pass
31
29cc1e22 32Pin = SrdIntEnum.from_str('Pin', 'SYNC BIT_CLK SDATA_OUT SDATA_IN RESET')
a83e259a
UH
33
34slots = 'TAG ADDR DATA 03 04 05 06 07 08 09 10 11 IO'.split()
35a = '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]
37Ann = SrdIntEnum.from_list('Ann', a)
38
39Bin = SrdIntEnum.from_str('Bin', 'FRAME_OUT FRAME_IN SLOT_RAW_OUT SLOT_RAW_IN')
93b92702
GS
40
41class 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