CS452 - Real-Time Programming - Spring 2012
Lecture 14 - Serial I/O, Debugging
Public Service Annoucements
- Assignment 4
- Performance measurements
Serial I/O
See pdf.
FIFO
Why do FIFOs exist in UARTS?
The Big Blunder
To use the FIFO effectively you must be able to turn off the transmitter
& receiver independently.
But look at UARTE in UARTxCtrl
- UART Enable.
- If this bit is set to 1, the UART is enabled.
- Data transmission and reception occurs for UART signals.
The Little Blunder
`It is assumed that various configuration registers for the UART are not
written more than once in quick succession, in order to insure proper
synchronization of configuration information across the implementation. Such
registers include UART1Ctrl and UART1LinCtrlHigh. ... In between the two
writes, at least two UARTCLK periods must occur. Under worst case conditions,
at least 55 HCLK periods must separate the two writes. The simplest way to
due [sic] this is to separate the two writes by 55 NOPs.'
Why does this occur?
- CPU clocked by CPU clock
- System buses clocked by several different clocks
- UART clocked by its own clock
- The clocks were not suitably synchronized
Why doesn't anybody care?
- UARTs are used at the beginning of the development process
- Once other I/O (ethernet, USB, etc.) is working, UARTs are no longer
used, except possibly by the boot loader
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
- 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 all the above interrupts are not asserted.
Three inputs to the ICU
- Transmit
- Receive
- Combined
These are adequate for interacting with the terminal, but not for
interacting with the train controller.
Easy way to use interrupts
Enable only combined; read UART registers to decide what to do.
Think of the receive and transmit parts of the UART as separate state
machines
- Base the state machine on bits in the status registers
- Make a separate state machine for flow control
Practical Advice
Until now you have been using busy-wait I/O for getting debugging output.
You would like to continue to have debugging output while you are
implementing interrupt-mediated I/O.
- There are two UART ports on the ARM board.
- Connect each one to a different terminal window on the terminal.
- Do busy-wait I/O to one for debugging while getting interrupt-mediated
I/O working on the other.
- Then do debugging I/O on the working serial server while you create and
debig the other server.
Hint. The serial server for the terminal must be a lot
more complex than the esrial server for the train controller.
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,
unconnected 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: