172 lines
9.3 KiB
HTML
172 lines
9.3 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
|
<HTML>
|
|
<HEAD>
|
|
<META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.21">
|
|
<TITLE> Serial HOWTO: How the Hardware Transfers Bytes </TITLE>
|
|
<LINK HREF="Serial-HOWTO-4.html" REL=next>
|
|
<LINK HREF="Serial-HOWTO-2.html" REL=previous>
|
|
<LINK HREF="Serial-HOWTO.html#toc3" REL=contents>
|
|
</HEAD>
|
|
<BODY>
|
|
<A HREF="Serial-HOWTO-4.html">Next</A>
|
|
<A HREF="Serial-HOWTO-2.html">Previous</A>
|
|
<A HREF="Serial-HOWTO.html#toc3">Contents</A>
|
|
<HR>
|
|
<H2><A NAME="how_hdw_xfers"></A> <A NAME="s3">3.</A> <A HREF="Serial-HOWTO.html#toc3">How the Hardware Transfers Bytes </A></H2>
|
|
|
|
<P> Below is an introduction to the topic, but for a more advanced
|
|
treatment of it see
|
|
<A HREF="Serial-HOWTO-18.html#fifo_">FIFOs</A>.</P>
|
|
|
|
<H2><A NAME="ss3.1">3.1</A> <A HREF="Serial-HOWTO.html#toc3.1">Transmitting</A>
|
|
</H2>
|
|
|
|
<P> Transmitting is sending bytes out of the serial port away from the
|
|
computer (output). Once you understand transmitting, receiving
|
|
(input) is easy to understand since it's similar. The first
|
|
explanation given here will be grossly oversimplified. Then more
|
|
detail will be added in later explanations. When the computer wants
|
|
to send a byte out the serial port (to the external cable) the CPU
|
|
sends the byte on the bus inside the computer to the I/O (Input
|
|
Output) address of the serial port. I/O is often written as just IO.
|
|
The serial port takes the byte, and sends it out one bit at a time (a
|
|
serial bit-stream) on the transmit pin of the serial cable connector.
|
|
For what a bit (and byte) look like electrically see
|
|
<A HREF="Serial-HOWTO-20.html#volt_shape">Voltage Waveshapes</A>.</P>
|
|
<P>Here's a replay of the above in a little more detail (but still very
|
|
incomplete). Most of the work at the serial port is done by the UART
|
|
chip (or the like). To transmit a byte, the serial device driver
|
|
program (running on the CPU) sends a byte to the serial port"s I/O
|
|
address. This byte gets into a 1-byte "transmit shift register" in
|
|
the serial port. From this shift register bits are taken from the
|
|
byte one-by-one and sent out bit-by-bit on the serial line. Then when
|
|
the last bit has been sent and the shift register needs another byte
|
|
to send, it could just ask the CPU to send it another byte. Thus would
|
|
be simple but it would likely introduce delays since the CPU might not
|
|
be able to get the byte immediately. After all, the CPU is usually
|
|
doing other things besides just handling the serial port.</P>
|
|
<P>A way to eliminate such delays is to arrange things so that the CPU
|
|
gets the byte before the shift register needs it and stores it in a
|
|
serial port buffer (in hardware). Then when the shift register has
|
|
sent out its byte and needs a new byte immediately, the serial port
|
|
hardware just transfers the next byte from its own buffer to the shift
|
|
register. No need to call the CPU to fetch a new byte.</P>
|
|
<P>The size of this serial port buffer was originally only one byte, but
|
|
today it is usually 16 bytes (more in higher priced serial ports).
|
|
Now there is still the problem of keeping this buffer sufficiently
|
|
supplied with bytes so that when the shift register needs a byte to
|
|
transmit it will always find one there (unless there are no more bytes
|
|
to send). This is done by contacting the CPU using an interrupt.</P>
|
|
<P>First we'll explain the case of the old fashioned one-byte buffer,
|
|
since 16-byte buffers work similarly (but are more complex). When the
|
|
shift register grabs the byte out of the buffer and the buffer needs
|
|
another byte, it sends an interrupt to the CPU by putting a voltage on
|
|
a dedicated wire on the computer bus. Unless the CPU is doing
|
|
something very important, the interrupt forces it to stop what it was
|
|
doing and start running a program which will supply another byte to
|
|
the port's buffer. The purpose of this buffer is to keep an extra
|
|
byte (waiting to be sent) queued in hardware so that there will be no
|
|
gaps in the transmission of bytes out the serial port cable.</P>
|
|
<P>Once the CPU gets the interrupt, it will know who sent the interrupt
|
|
since there is a dedicated interrupt wire for each serial port (unless
|
|
interrupts are shared). Then the CPU will start running the serial
|
|
device driver which checks registers at I/0 addresses to find out what
|
|
has happened. It finds out that the serial's transmit buffer is empty
|
|
and waiting for another byte. So if there are more bytes to send, it
|
|
sends the next byte to the serial port's I/0 address. This next byte
|
|
should arrive when the previous byte is still in the transmit shift
|
|
register and is still being transmitted bit-by-bit.</P>
|
|
<P>In review, when a byte has been fully transmitted out the transmit
|
|
wire of the serial port and the shift register is now empty the
|
|
following 3 things happen in rapid succession:</P>
|
|
<P>
|
|
<OL>
|
|
<LI> The next byte is moved from the transmit buffer into
|
|
the transmit shift register</LI>
|
|
<LI> The transmission of this new byte (bit-by-bit) begins</LI>
|
|
<LI> Another interrupt is issued to tell the device driver to send
|
|
yet another byte to the now empty transmit buffer</LI>
|
|
</OL>
|
|
</P>
|
|
<P>Thus we say that the serial port is interrupt driven. Each time the
|
|
serial port issues an interrupt, the CPU sends it another byte. Once
|
|
a byte has been sent to the transmit buffer by the CPU, then the CPU
|
|
is free to pursue some other activities until it gets the next
|
|
interrupt. The serial port transmits bits at a fixed rate which is
|
|
selected by the user (or an application program). It's sometimes
|
|
called the baud rate. The serial port also adds extra bits to each
|
|
byte (start, stop and perhaps parity bits) so there are often 10 bits
|
|
sent per byte. At a rate (also called speed) of 19,200 bits per
|
|
second (bps), there are thus 1,920 bytes/sec (and also 1,920
|
|
interrupts/sec).</P>
|
|
<P>Doing all this is a lot of work for the CPU. This is true for many
|
|
reasons. First, just sending one 8-bit byte at a time over a 32-bit
|
|
data bus (or even 64-bit) is not a very efficient use of bus width.
|
|
Also, there is a lot of overhead in handing each interrupt. When the
|
|
interrupt is received, the device driver only knows that something
|
|
caused an interrupt at the serial port but doesn't know that it's
|
|
because a character has been sent. The device driver has to make
|
|
various checks to find out what happened. The same interrupt could
|
|
mean that a character was received, one of the control lines changed
|
|
state, etc.</P>
|
|
<P>A major improvement has been the enlargement of the buffer size of the
|
|
serial port from 1-byte to 16-bytes. This means that when the CPU
|
|
gets an interrupt it gives the serial port up to 16 new bytes to
|
|
transmit. This is fewer interrupts to service but data must still be
|
|
transferred one byte at a time over a wide bus. The 16-byte buffer is
|
|
actually a FIFO (First In First Out) queue and is often called a FIFO.
|
|
See
|
|
<A HREF="Serial-HOWTO-18.html#fifo_">FIFOs</A> for details about the FIFO along
|
|
with a repeat of some of the above info.</P>
|
|
|
|
<H2><A NAME="ss3.2">3.2</A> <A HREF="Serial-HOWTO.html#toc3.2">Receiving</A>
|
|
</H2>
|
|
|
|
<P> Receiving bytes by a serial port is similar to sending them only
|
|
it's in the opposite direction. It's also interrupt driven. For the
|
|
obsolete type of serial port with 1-byte buffers, when a byte is fully
|
|
received from the external cable it goes into the 1-byte receive
|
|
buffer. Then the port gives the CPU an interrupt to tell it to pick
|
|
up that byte so that the serial port will have room for storing the
|
|
next byte which is currently being received. For newer serial ports
|
|
with 16-byte buffers, this interrupt (to fetch the bytes) may be sent
|
|
after 14 bytes are in the receive buffer. The CPU then stops what it
|
|
was doing, runs the interrupt service routine, and picks up 14 to 16
|
|
bytes from the port. For an interrupt sent when the 14th byte has
|
|
been received, there could be 16 bytes to get if 2 more bytes have
|
|
arrived since the interrupt. But if 3 more bytes should arrive
|
|
(instead of 2), then the 16-byte buffer will overrun. It also may
|
|
pick up less than 14 bytes by setting it up that way or due to
|
|
timeouts. See
|
|
<A HREF="Serial-HOWTO-18.html#fifo_">FIFOs</A> for more details.</P>
|
|
|
|
<H2><A NAME="ss3.3">3.3</A> <A HREF="Serial-HOWTO.html#toc3.3">The Large Serial Buffers</A>
|
|
</H2>
|
|
|
|
<P> We've talked about small 16-byte serial port hardware
|
|
buffers but there are also much larger buffers in main memory. When
|
|
the CPU takes some bytes out of the receive buffer of the hardware, it
|
|
puts them into a much larger (say 8k-byte) receive buffer in main
|
|
memory. Then a program that is getting bytes from the serial port
|
|
takes the bytes it's receiving out of that large buffer (using a
|
|
"read" statement in the program).</P>
|
|
<P>A similar situation exists for bytes that are to be transmitted. When
|
|
the CPU needs to fetch some bytes to be transmitted it takes them out
|
|
of a large (8k-byte) transmit buffer in main memory and puts them into
|
|
the small 16-byte transmit buffer in the hardware. And when a program
|
|
wants to send bytes out the serial port, it writes them to this large
|
|
transmit buffer.</P>
|
|
<P>Both of these buffers are managed by the serial driver. But the
|
|
driver does more than just dealing with these buffers. It also does
|
|
limited filtering (minor modifications) of data passing thru these buffers
|
|
and listens for control certain characters. All this is configurable
|
|
using the
|
|
<A HREF="Serial-HOWTO-11.html#stty_">Stty</A> program.</P>
|
|
|
|
<HR>
|
|
<A HREF="Serial-HOWTO-4.html">Next</A>
|
|
<A HREF="Serial-HOWTO-2.html">Previous</A>
|
|
<A HREF="Serial-HOWTO.html#toc3">Contents</A>
|
|
</BODY>
|
|
</HTML>
|