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