Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 21 Jul 1997 11:28:44 +0930 (CST)
From:      Michael Smith <msmith@atrad.adelaide.edu.au>
To:        luigi@iet.unipi.it (Luigi Rizzo)
Cc:        hackers@FreeBSD.ORG, multimedia@FreeBSD.ORG
Subject:   Re: dma handling in the sound driver
Message-ID:  <199707210158.LAA20128@genesis.atrad.adelaide.edu.au>
In-Reply-To: <199707191437.QAA01053@prova.iet.unipi.it> from Luigi Rizzo at "Jul 19, 97 04:37:50 pm"

next in thread | previous in thread | raw e-mail | index | archive | help
Luigi Rizzo stands accused of saying:
>
> - with two DMA buffers, we can refill one buffer while the other one
>   is in use by the DMA engine. We can still have troubles if we start
>   a long refill near the end of operation of DMA on the other buffer,
>   but this problem can be minimized (but not avoided; if we are late,
>   we are late, no matter how many buffers we have!)

This is the traditional double-buffered approach.  You go on to
describe a triple-buffered approach which is more suited to the high
latency that is often encountered in multiprocessing situations.

> In our implementation, we use a single memory block structured as
> three logical, variable-size, buffers: one in use by the dma engine,
> the next one ready for use by the dma (already filled up), the last
> one free for refills.
> 
>             dp,dl         rp,rl           fp,fl
>     +-------+-------------+---------------+------+
>     | free  | used by dma | ready for use | free |
>     +-------+-------------+---------------+------+
> 
> Both the "ready" and "free" areas can wrap around the end of the
> buffer.

I presume that the plan here is that the host DMA controller loops
endlessly over this buffer in autoinit mode?

If not, there's no apparent need for such a complex approach; you can
just use three separate buffers each sized suitably to cover the
latency involved in filling the next.

Another alternative would be to simply use an endlessly-recirculating
DMA buffer of appropriate size thus :

  DMA
   V
   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXOOOOOOOOOOOOOOOOOO

Some data has been written to the sound device.  Regardless of how
much data you have, you start the DMA going.

                 DMA
                  V
   OOOOOOOOOOOOOOOXXXXXXXXXXXXXXXXXXXOOOOOOOOOOOOOOOOOO

Here the DMA is in progress, consuming data.  A selecting writer would be
able to write here.
 
                           DMA
                            V
   YYYYOOOOOOOOOOOOOOOOOOOOOXXXXXXXXXYYYYYYYYYYYYYYYYYY

... and more data has arrived.  The key to keeping more data in the buffer
than is consumed by the looping DMA is to make sure that any selecting
writer is woken up often enough to keep you busy.  In order to do this,
you need something that interrupts you more than once per DMA loop.

At 44kHz, 16-bit stereo you are looking at 160kB/sec throughput, which
equates to 1.6kB per possible wakeup().  This isn't too hard to manage
really; a 64kB recirculating buffer will give you 400ms of audio, or 
a 200ms mean wakeup time.

You can play some more neat games.  In the original first case :

  DMA
   V
   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXOOOOOOOOOOOOOOOOOO
                                     ^
                                     TC

you set the terminal count short of the end of the buffer.  This avoids
your having to worry about the DMA running into unfilled buffer space.

In the later case, where the buffer contents have wrapped :

                           DMA
                            V
   YYYYOOOOOOOOOOOOOOOOOOOOOXXXXXXXXXYYYYYYYYYYYYYYYYYY
                                                       ^
                                                       TC

TC is set to the end of the buffer, and the autoinit bit is set, so
that it will loop back to the start.  When it does loop, you'll get an
interrupt, and you can reprogram TC again :

  DMA
   V
   YYYYOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
       ^
       TC

If an application is too slow to respond to your waking it up, then
there's really nothing more you can do.  As Amancio has observed, many
applications will want to write small amounts of data on a regular
basis.  A timer event run once every 1/hz seconds can easily monitor
the progress of the DMA and update the buffer tail pointer & wake up
any writers.

In this model, the overhead of uiomove is effectively irrelevant; data
is solicited from the application as early as is possible.

I'm not sure if this actually helps you...

> Luigi Rizzo                     Dip. di Ingegneria dell'Informazione

-- 
]] Mike Smith, Software Engineer        msmith@gsoft.com.au             [[
]] Genesis Software                     genesis@gsoft.com.au            [[
]] High-speed data acquisition and      (GSM mobile)     0411-222-496   [[
]] realtime instrument control.         (ph)          +61-8-8267-3493   [[
]] Unix hardware collector.             "Where are your PEZ?" The Tick  [[



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199707210158.LAA20128>