CS452 - Real-Time Programming - Spring 2017

Lecture 15 - Serial I/O, Debugging

Pubilc Service Announcements

  1. Due date for kernel 4: 12 June, 2017.

Serial I/O Interrupts

Five interrupts in the device

These interrupts are separately enabled and disabled.

  1. Transmit
  2. Receive
  3. Modem status
  4. Receive timeout
  5. Combined

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

  1. Transmit
  2. Receive
  3. 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

Implementation of Serial I/O


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

Providing I/O for the terminal


  1. Enable RcvRdy, XmitRdy interrupts in UART.
  2. Enable combined interrupt in ICU


Notifier EVT_BL on RcvRdy event

  1. Interrupt occurs, AwaitEvent returns with received byte.
  2. Notifier sends byte to server
  3. Server replies,
  4. Notifier returns to the EVT_BL state
  5. After a while server runs again
  6. Server checks GetQ
  7. If ( ! GetQ_Empty )
  8. Else put byte in RcvQ

When server receives Get request from client

  1. Server checks RcvQ
  2. If ( ! RcvQ_Empty )
  3. Else put client in GetQ


Transmitting is a little more tricky because two conditions must be met.

  1. Byte available to transmit
  2. Transmit holding register empty

Assume we put conjunction detection in the server

When server receives Put request from client

  1. Is Notifier ready?
  2. If ( Ready ) Reply( notifier, byte ); Reply( client, ... )
  3. Else insert byte in XmitQ; Reply( client, ... )

When server receives Ready request from Notifier

  1. If ( XmitQEmpty ) Mark notifier ready
  2. 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?

  1. Enable XmitRdy interrupt
  2. byte = AwaitEvt( XmitRdy )
  3. Disable XmitRdy interrupt in UART
  4. Send( server, ready, byte )
  5. Write byte on UART
  6. 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


As Above


For transmitting three conditions must be set

  1. Byte available to transmit
  2. Holding register empty
  3. CTS asserted

Serial I/O Timing Inconsistencies

CTS timing.

What you expect to occur when sending to the train controller

  1. The train application puts a byte in the hold register. The byte is moved into the shift register and shifted out.
  2. The stop bit is transmitted.
  3. A train xmit interrupt occurs.
  4. The train application checks CTS.
  5. 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.

  1. The train application puts a byte in the hold register, which is loaded and shifted out.
  2. The stop bit is transmitted.
  3. Two things happen in parallel.
    1. 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.
    2. 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.
  4. 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 )


int Put( int server-tid, int port, char c )

These are wrappers for sends to one or more serial servers.

How should we handle a terminal?


  1. Line editing
  2. Echo
    Either Or

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.

Tools you might use to assist debugging


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.


What does it do?

How do you get it started?

Breakpoint is a special case of a particular sort of tool that is very common.

Return to: