CS452 - Real-Time Programming - Spring 2017
Lecture 15 - Serial I/O, Debugging
Pubilc Service Announcements
-
Due date for kernel 4: 12 June, 2017.
Serial I/O Interrupts
Five interrupts in the device
These interrupts are separately enabled and disabled.
- Transmit
- FIFO enabled
- Asserted when transmit FIFO is less than half full.
- Cleared when transmit FIFO is more than half full.
- FIFO disabled
- Asserted when holding register is empty
- Cleared on write to the holding register
-
How do you clear this interrupt if you have nothing to transmit?
-
Not conditioned by enable.
- Receive
- FIFO enabled
- Asserted when receive FIFO is half full
- Cleared when receive FIFO is read to less than half full.
- FIFO disabled
- Asserted when receive buffer is full
- Cleared when receive buffer is read
- Modem status
-
Asserted when hardware flow control bits change
-
Cleared when the modem status register is written
- Receive timeout
- Asserted when receive FIFO is not empty and 32 bit periods pass
with no new data
- Cleared when all data has been read from FIFO
- Combined
- OR of the four above interrupts
- Asserted when at least one of the above interrupts is asserted
-
Cleared when none of the above interrupts are asserted.
Registers of the UART
Offset |
Register |
Function |
0x00 |
data |
Write: data to transmit. Read: received data. |
0x04 |
Interrupt enable |
|
0x08 |
Status/control |
Read: interrupts asserted.Write: FIFO control. |
0x0c |
Line control |
|
0x10 |
Modem control |
|
0x14 |
Line status |
|
|
0x18 |
Modem status. |
|
|
0x1c |
Scratch. |
|
Three inputs to the PIC
- Transmit
- Receive
- Combined
Easy way to use interrupts
Enable only combined; read UART register to decide which interrupt(s)
are asserted.
Think of the receive and transmit parts of the UART as separate state
machines
-
Base the state machine on interrupt occurrence and bits in the
status registers
-
Make a separate state machine for flow control
Implementation of Serial I/O
Assume.
-
The FIFO is turned off.
-
Buffering is done in the server.
The simplest way to handle the interrupts is to turn on only the
combined interrupt and then look at the registers of the device.
To identify the current interrupt
Read UARTxIntIDIntClr
at 0x800[bc]001c
- One of eight registers for interacting with the UART
- Four bits, one for each interrupt
- (Write anything to this register to clear the modem status
interrupt)
Providing I/O for the terminal
Initialization
- Enable RcvRdy, XmitRdy interrupts in UART.
- Enable combined interrupt in ICU
Receiving
Notifier EVT_BL on RcvRdy event
-
Interrupt occurs, AwaitEvent returns with received byte.
-
Notifier sends byte to server
-
Server replies,
-
Notifier returns to the EVT_BL state
-
After a while server runs again
-
Server checks GetQ
-
If ( ! GetQ_Empty )
-
Extract client from GetQ
-
Reply( client, byte )
-
Else put byte in RcvQ
When server receives Get request from client
- Server checks RcvQ
- If ( ! RcvQ_Empty )
- Extract byte from RcvQ
- Reply( client, byte )
- Else put client in GetQ
Transmitting
Transmitting is a little more tricky because two conditions must
be met.
- Byte available to transmit
- Transmit holding register empty
Assume we put conjunction detection in the server
When server receives Put request from client
- Is Notifier ready?
- If ( Ready ) Reply( notifier, byte ); Reply( client, ... )
- Else insert byte in XmitQ; Reply( client, ... )
When server receives Ready request from Notifier
- If ( XmitQEmpty ) Mark notifier ready
- Else Extract byte from XmitQ; Reply( notifier, byte ); Reply( client
)
The Notifier
The procedure described here breaks an abstraction barrier. Is there
any other way to do what you want?
- Enable XmitRdy interrupt
- byte = AwaitEvt( XmitRdy )
- Disable XmitRdy interrupt in UART
- Send( server, ready, byte )
- Write byte on UART
- Go to top
This procedure assumes that the transmit bit never resets once it is set.
Is this true?
Conjunction detection and buffer could just as easily be in the
Notifier
Providing I/O for the train controller
Receiving
As Above
Transmitting
For transmitting three conditions must be set
- Byte available to transmit
- Holding register empty
- CTS asserted
Serial I/O Timing Inconsistencies
CTS timing.
What you expect to occur when sending to the train controller
-
The train application puts a byte in the hold register. The
byte is moved into the shift register and shifted out.
-
The stop bit is transmitted.
-
A train xmit interrupt occurs.
-
The train application checks CTS.
-
If CTS is set, you put the next byte in the hold register.
Sometimes an implementation based on this expectation surprises
you. In reality what happens when you are sending to the train
controller the following set of events occur.
-
The train application puts a byte in the hold register, which is
loaded and shifted out.
-
The stop bit is transmitted.
-
Two things happen in parallel.
-
In the train controller,
-
The train controller sees the stop bit.
-
Then, probably on the next clock edge, it latches the
character and negates CTS.
-
After one or two clock cycles the byte is clear of the
train UART, and CTS is re-asserted.
-
In the ARM CPU
-
The train xmit interrupt occurs.
-
The train application checks if there another byte to
send, and
-
if there is, it checks the CTS bit in the modem status
register (CTS-sr). What value does it read?
-
When the stop bit was transmitted CTS-sr was asserted.
-
Then, the train controller negates the CTS state
internally, probably on the next edge of its UART
clock; the ARM UART latches the negated signal on the edge of its UART clock,
and into its CTS-sr on the next edge of the peripheral bus clock. From this
point CTS-sr is negated.
-
Finally, when CTS is re-asserted in the train controller UART and has been
latched a second time in the ARM UART CTS-rs is re-asserted.
The train application is assumed to check CTS after the
train controller has negated it. It may even be unaware that
CTS is negated for a short while. Regardless, because the two
sets of events happen in parallel the result is non-deterministic.
-
If CTS is set, the train application puts the next byte in
the hold register.
Whether the final action succeeds, drops a character or
crashes the train controller is determined by the whims of the
gods.
How do you solve this problem is for you to discover. Hint, interrupts
occur in a deterministic order.
Primitives for Serial I/O
We are supposed to support
int Get( int server-tid, int port )
and
int Put( int server-tid, int port, char c )
These are wrappers for sends to one or more serial servers.
-
On Get the server blocks the client until data is available
-
Does a non-blocking TryGet give you anything you cannot do
with task structure?
-
Put is non-blocking. The only guarantee is that the character
has been added to a buffer of characters to be transmitted
some time in the future.
How should we handle a terminal?
Issues
-
Line editing
- undefined intermediate states
- well-defined trigger
-
Echo
Either
-
one server per channel,
-
passing characters from one notifier to the other
Or
- courier connecting receive server to transmit server
- What characteristics does the terminal have? termcap follies
Many other issues come up below as we consider possible task
structures.
How ahould we handle the train controller
Bandwidth of communication with the train controller will probably
be the limiting factor in your trains project.
-
Use it wisely, which means as little as possible.
-
Any time you can substitute computation for communication you should
do so.
Tools you might use to assist debugging
Redboot
The memory contents are not wiped by reset. Some of the most
difficult errors can be detected only by using the contents of
memory after a reset. Produce useful results by inserting
str pc, <magic location>
many places in your code. Redboot can tell you the contents of
the magic places after the program crashes. Then, with the
assistance of a load map, you can find out where you were in which
code when the problem occurred.
You can often find out this information by looking at the garbage
output that often appears at the bottom of the screen. One set
of eight characters is the hex address of the instruction that
could not be executed. With a load map and assembly listings you
can find the instruction on which the program died.
In RedBoot you can, in principle, trace any of the kernel data
structures by examining the kernel's stack, but it's extremely
arduous to do so.
Breakpoint
What does it do?
-
takes a snapshot of the system
-
The body of the program must remain undisturbed.
-
This means that computation, including response to interrupts,
must stop, or it isn't a snapshot.
- It's easy to make a mistake and have some parts of the
program continuing to run while you are interrogating the
system.
-
provides interactive tools for examining kernel data structures,
such as
- task descriptors
- lists and queues
- stacks, including the program counter and local variables, of
individual tasks
- restart system immediately afterwards
- If you want to continue where processing stopped you must make
certain that all state is saved when you enter Beakpoint
and restored when you leave it. What about pending interrupts?
You can't stop the entire universe!
- Otherwise you can re-enter RedBoot.
How do you get it started?
- function call, which you insert in your code when compiling.
- The easiest and fastest form to implement.
- Having the call as part of ASSERT is common.
- Has to exit to RedBoot. Why? (Jump to x00.)
- system call instead of function call, which respects the kernel/user
distinction.
- an exception triggered externally
- at initialization
- Set up the system so that an external event will generate an
interrupt.
-
E.g. attach a button to PDIO on the third connector, set up ICU.
- at run-time
- Trigger the interrupt
- Switch to Breakpoint in the event handler
- Either exit to RedBoot,
- Or clean up pending interrupts and resume execution.
Breakpoint is a special case of a particular sort of tool that is very
common.
- condition occurs => information is made available
- breakpoint provides the information interactively (`interactively' =
`on the time scale of the user')
- it can stop the system completely. How?
- but it has limited ability to stop the real world
Return to: