CS452 - Real-Time Programming - Winter 2013
Lecture 14 - Implementation of Serial I/O
Pubilc Service Annoucements
- Due date for kernel four.
- PLG/IRG Potential Spring 2013 Job Opportunity for CS 452 Students
- Gord and I are looking for a student who is fairly proficient in
C/C++, is reasonably confident in their math abilities, and would be
interested in assisting us (read: do most of the heavy lifting)
inimproving/modifying/upgrading the Wumpus Search Engine
(http://www.wumpus-search.org/ developed by one of Charlie Clarke's
former students) with the intent of improving user friendliness of
the system and to meet the current needs of the Information Retrieval
Group.
- So if you know of anyone in 452 (or else where) who fits the bill
then please have them contact either myself or Gord.
- Thanks, Adam Roegiest <aroegies@plg.uwaterloo.ca>.
Implementation of Serial I/O
FIFO is assumed to be turned off; buffers are assumed to be 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 RCV_BL on RcvRdy event
- Interrupt occurs, AwaitEvent returns with byte.
- Notifier sends byte to server
- Server replies, Notifier returns to initial state
- Server checks GetQ
- If ( ! GetQ_Empty )
- Extract client from GetQ
- Reply( client, byte )
- Else insert byte in RcvQ
When server receives Get request from client
- Server checks RcvQ
- If ( ! RcvQ_Empty )
- Extract byte from RcvQ
- Reply( client, byte )
- Else insert 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
- Is XmitQ empty?
- If ( Empty ) Mark notifier ready
- Else Extract byte from XmitQ; Reply( notifier, byte ); Reply( client
)
The Notifier
- Enable XmitRdy interrupt
- byte = AwaitEvt( XmitRdy )
- Disable XmitRdy interrupt in UART
- Send( server, ready, byte )
- Write byte on UART
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
Task Structure
We are supposed to support
int Get( int port )
and
int Put( 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
You will probably want something more complex in addition to Get and
Put.
How many servers and notifiers?
|
one server |
two servers |
four servers |
| one
notifier
|
likely queue
congestion in
server
|
|
likely queue
congestion
in notifer
|
| two
notifiers
|
one per channel?
one per direction?
|
how should they
be paired?
|
|
| four
notifiers
|
certain queue
congestion
in server
|
likely queue
congestion in
server
|
best performance,
most tasks
|
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.
Debugging Real-time Programs
The most common set of debugging tools used by experienced programmers is
the oldest: printf, grep & stack trace.
- The power of these tools is greatly enhanced by strong conventions in
code formatting.
Debugging real-time programs, at its base, is just the same as any other
debugging, and just the same as empirical science.
- Gather data.
- Create a model that explains the data
- Test the model
- If the model is not correct, go to 1.
- Remember that the model is ALWAYS provisional: data collected later may
invalidate it, no matter how much data has confirmed it.
But real-time programs are harder to debug. Very few programs are entirely
free of critical races, which are the worst type of bug, lurking for weeks
months or years in seemingly correct code, then appearing when innocuous,
believed to be independent code changes occur.
Critical Races
There is no known method for eliminating critical races.
- Synchronizing everything, which seems to be an obvious solution, kills
performance because it removes flexibility from the execution.
It is, in principle, impossible to test away critical races. Why?
- When three trains run continuously for ten minutes, how many events
occur in the real world?
- How many possible orders are there for these events?
- Re-ordering isn't even necessary for a critical race to occur, just
getting too close in time.
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. Then, with the assistance of a load map, you can
find out where you were in which code when the problem occurred.
In RedBoot you can, in principle, trace any of the kernel
Stack Trace
In single-threaded programs this is often the most useful tool.
- Anything that terminates execution abnormally prints the set of active
stack frames
- Minimal version
- name of calling function
- line number of call
- Extreme version
- values of arguments
- values of local variables
What is the equivalent of a stack trace in a real-time multi-tasking
environment?
Breakpoint
What does it do?
- snapshot of the system
- This means that computation, including respose to interrupts, must
stop, or it isn't a snapshot.
- 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. (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 the external event will generate an
exception
- 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
Getting information closer to real-time.
Return to: