becb43fcf4
* Import my "Installation for the Impatient" to the install section. * Move Booting and memory use into a "Tech Topics" chapter; add DMA information. (Frank Durda IV) * Bring in ESDI section. (Wilko Bulte) * Bring in MD5/DES section. (Garrett Wollman) * Fix a couple problems with LaTeX output.
106 lines
5.3 KiB
Plaintext
106 lines
5.3 KiB
Plaintext
<!-- $Id$ -->
|
|
<!-- The FreeBSD Documentation Project -->
|
|
|
|
<sect><heading>PC DMA<label id="dma"></heading>
|
|
|
|
<p><em>Contributed by &a.uhclem;.<newline>
|
|
31 August 1995.</em>
|
|
|
|
Posted to <htmlurl url="mailto:hackers@freebsd.org"
|
|
name="freebsd-hackers@freebsd.org">:
|
|
<quote>
|
|
<p><em>Yes, as long as `single mode' is appropriate for you, there's no need
|
|
to worry about TC. TC is intented for continuous mode. Well, i've
|
|
just noticed that the PC DMAC cannot even generate an interrupt when
|
|
ready... hmm, go figure, the Z80 DMAC did it.</em>
|
|
<p><em>And yes, for `single mode', the masking trick will do it. The
|
|
peripheral device will issue a DRQ signal for each transfered
|
|
byte/word, and masking would prevent the DMAC from accepting new DRQs
|
|
for this channel. Aborting a continuous mode transfer would not be so
|
|
easy (or even impossible at all).</em>
|
|
</quote>
|
|
|
|
Actually, masking is the correct procedure for all transfer modes on the
|
|
8237, even autoinit mode, which is frequently used for audio operations
|
|
since it allows seamless DMA transfers with no under/overruns.
|
|
|
|
You are generally correct about TC. All the TC signal does is
|
|
when the counter on any channel in the DMA controller goes from
|
|
one to zero, TC is asserted. What the peripherals are supposed
|
|
to if they want to generate an interrupt when the transfer is
|
|
through, is that peripheral device is supposed to look at
|
|
<tt>(-DACK%d && TC && DEVICE_DMA_ACTIVE)</tt> and then
|
|
latch an <tt>IRQ%d</tt> for the 8259 interrupt controller. Since there is
|
|
only one TC signal, it is important that only the peripheral who
|
|
is transferring data at that moment honor the TC signal.
|
|
|
|
The host CPU will eventually investigate the interrupt by having some driver
|
|
poll the hardware associated with the peripheral, NOT the DMA controller.
|
|
If a peripheral doesn't want an interrupt associated with the DMA counter
|
|
reaching zero, it doesn't implement the circuitry to monitor TC.
|
|
|
|
Some sound cards realize that when the TC hits zero it means the DMA
|
|
is now idle and that is really too late, so they don't use TC and
|
|
instead allow the driver to program in a local counter value, which
|
|
is usually set lower than the value programmed into the DMA. This means
|
|
the peripheral can interrupt the CPU in advance of the DMA "running dry",
|
|
allowing the CPU to be ready to reprogram the DMA the instant it finishes
|
|
what it is doing, rather than incurring the latency later.
|
|
|
|
This also means that two or more different devices could share a
|
|
DMA channel, by tristating <tt>DRQ%d</tt> when idle and only
|
|
honoring <tt>-DACK%d</tt> when the device knows it is expecting
|
|
the DMA to go active. (Iomega PC2B boards forgot this minor
|
|
point and will transfer data even if they are not supposed to.)
|
|
|
|
|
|
So, if you want to abort a 8237 DMA transfer of any kind, simply mask the
|
|
bit for that DMA channel in the 8237. Note: You can't interrupt an individual
|
|
transfer (byte or burst) in progress. Think about it... if the DMA is
|
|
running, how is your OUT instruction going to be performed?
|
|
The CPU has to be bus master for the OUT to be performed.
|
|
|
|
Since the 8237 DMA re-evaluates DMA channel priorities constantly, even if
|
|
the DMA had already asserted HOLD (to request the bus from the CPU) when
|
|
the OUT actually took place, the processor would still grant the bus to the
|
|
DMA controller. The DMA controller would look for the highest-priority
|
|
DMA source remaining (your interrupt is masked now) at that instant,
|
|
and if none remained, the DMA will release HOLD and the processor will
|
|
get the bus back after a few clocks.
|
|
|
|
There is a deadly race condition in this area, but if I remember right,
|
|
you can't get into it via mis-programming the DMA, UNLESS you cause the DMA
|
|
controller to be RESET. You should not do this. Effectively the CPU
|
|
can give up the bus and the DMA doesn't do anything, including giving the
|
|
bus back. Very annoying and after 16msec or so, all is over since
|
|
refresh on main memory has started failing.
|
|
|
|
So, mask the DMA controller, then go do what you have to do to get the
|
|
transfer aborted in the peripheral hardware. In some extremely stupid
|
|
hardware (I could mention a few), you may have to program the DMA to
|
|
transfer one more byte to a garbage target to get the peripheral hardware
|
|
to go back to an idle state. Most hardware these days isn't that
|
|
stupid.
|
|
|
|
Technically, you are supposed to mask the DMA channel, program the other
|
|
settings (direction, address, length, etc), issue commands to the
|
|
peripheral and then unmask the DMA channel once the peripheral commands have
|
|
been accepted. The last two steps can be done out of order without
|
|
harm, but you must always program the DMA channel while it is masked to
|
|
avoid spraying data all over the place in the event the peripheral
|
|
unexpected asserts <tt>DRQ%d</tt>.
|
|
|
|
If you need to pad-out an aborted buffer, once you have masked the
|
|
DMA, you can ask it how many bytes it still had to go and what
|
|
address it was to write to next. Your driver can then fill in the
|
|
remaining area or do what needs to be done.
|
|
|
|
|
|
Don't forget that the 8237 was designed for use with the 8085 and
|
|
really isn't suited to the job that IBM gave it in the original PC.
|
|
That's why the upper eight bits of DMA addressing appear to be lashed-on.
|
|
They are. Look at the schematics of the original PC and you will
|
|
the upper bits are kept in external latches that are enabled whenever
|
|
the DMA is too. Very kludgy.
|
|
|