High Speed Serial Port Design Pattern
Embedded processors now-a-days are connected with each other via high speed serial links. There links can range from 1 Mbps to 100 Mbps. At these high link speeds, the byte by byte transfers described in the serial port design pattern prove inadequate. High speed transfers require devices that can directly transfer data to and from memory without interrupting the processor.
This design pattern covers interfacing techniques with high speed serial communication devices. The main objective is to encapsulate the interface with the device and provide a hardware independent interface to the high speed serial port.
Also Known As
- LAN Interface
- Intelligent Device Interface
- DMA Serial Device
The main motivation for development of this design pattern is to minimize dependency on hardware. Very often the hardware team decides to change the interface devices due to cost, end-of-life or functionality improvements. This involves a costly software porting exercise. High speed serial port design pattern encapsulates DMA configuration, register interfacing and interrupt handling specific to a device. Change in the device will just result in changes to just the classes involved in implementing this design pattern.
This pattern is applicable to all serial devices that involve DMA (direct memory access) transfers to and from the device. In such devices serial transmission and reception are completely handled by the serial device. The device operates as a bus master and transfers data to and from the memory without main processor intervention.
Serial Port is implemented with the SerialPort and SerialPortManager classes. The SerialPortManager maintains an array of SerialPort objects. Each SerialPort object manages the transmit and receive buffers. The SerialPortManager class also implements the interrupt service routine.
As mentioned earlier, the serial port is intelligent. The device needs to be programmed with the addresses of the transmit and receive message queues. Once these addresses have been programmed, the device automatically reads the buffer header to determine the current state of the buffer. For example, when the device finishes transmission of a buffer, it reads the buffer header for the next buffer to determine there is a new message ready for transmission. If a new message is found, the device initiates transmission immediately, without involving the processor.
The key participants in this pattern are:
- Serial Port Manager: Manages all the Serial Ports on the board.
- Interrupt Service Routine: Implemented as a static method in Serial Port Manager.
- Serial Port: Handles the interface with a single serial port device. This class also contains the transmit and receive buffers.
- Transmit Queue: This queue contains messages awaiting transmission on the serial port.
- Receive Queue: Messages received on the serial link are stored in this queue.
The interactions between the participants are shown in the figure below:
Implementing the Serial Port design pattern keeps the hardware dependent code confined to a few classes in the system. This simplifies the software port to new hardware.
The implementation of this design pattern is explained in terms of handling of message transmission and reception. The important point to note here is that the code executing in the context of the ISR is kept to the minimum. All the CPU intensive operations are carried out at the task level.
Transmitting a Message
- SerialPortManager's constructor installs the InterruptServiceRoutine().
- Serial Port's constructor initializes the interrupts so that the transmitter empty interrupt is disabled and the receiver ready interrupt is enabled.
- A message is enqueued to the SerialPort by invoking the HandleTxMessage() method.
- The method enqueues the message in the Transmit Queue and checks if this is the first message in the queue.
- Since this is the first message in the queue, the message is removed from the queue and copied into a transmission buffer and the "ready for transmission" flag is set.
- The device periodically becomes a bus master, polling for the "ready for transmission" flag in the first transmission buffer.
- The flag is set, so the device begins transmission of the buffer.
- When all bytes of the message have been transmitted, the device set the "finished transmission" bit in the buffer header.
- The device checks the next buffer to determine if it is ready for transmission.
- In this scenario, no other buffer is ready for transmission. Device raises the transmission complete interrupt. (If more messages were enqueued, the device would have automatically started transmitting the buffer).
- The InterruptServiceRoutine() is invoked.
- The ISR polls the SerialPorts to select the interrupting device.
- The HandleInterrupt() method of the SerialPort is invoked.
- SerialPort checks the interrupt status register to determine the source of the interrupt.
- This is a transmit interrupt, so the HandeTxInterrupt() method is invoked.
- A transmission complete event is sent to the task.
- This event is routed by the SerialPortManager to the SerialPort.
- SerialPort checks if the transmit queue has any more messages.
- If a message is found, message transmission of the new message is initiated.
Receiving a Message
- When the device detects the start of a new message, it accesses the receive buffers and checks the "free buffer" bit in the buffer header.
- The device finds a free buffer, so it starts DMA operations to copy all the received bytes into the designated buffer.
- The device raises an interrupt when message reception is completed. It also sets the "received message" bit in the buffer header. (If another message reception starts, the device will automatically start receiving that message in the next buffer)
- At this point a message receive complete event is dispatched to the task.
- The Serial Port's event handler allocates memory for the received message and writes the new message into the receive queue.
- Then it cleans up the receive buffer by setting the "free buffer" bit in the buffer header.
Sample Code and Usage
Here is the code for a typical implementation of this pattern:
This pattern is used to design serial port interfaces with intelligent devices which are capable of performing DMA operations to manage buffers in the memory. These devices only interrupt the processor when complete messages have been received/transmitted.