Hardware Device Design Pattern
We have already looked at classes that provide a high level interface to the underlying hardware (e.g. Serial Port). Here we will look at the design of classes corresponding to the individual devices. The main objective is to keep all register programming information at one place.
The Hardware Device Design Pattern encapsulates the actual hardware device being programmed. The main idea is to encapsulate device register programming and bit manipulation into a single class dealing with the device.
Also Known As
- Hardware Interface
- Peripheral Interface
Very often the lowest level of code that interfaces with the hardware is difficult to understand and maintain. One of the main reasons for this is the idiosyncrasies of register level programming model of hardware devices. Very often devices require registers to be accessed in a certain sequence. Defining a class to represent the device can go a long way in simplifying the code by decoupling the low level code and register manipulation.
Another motivation for this design pattern is skill sets. Often details about intricacies of register programming in devices are understood only by the persons familiar with the hardware design. Many times other low level code might be written by software engineers with just basic understanding of hardware.
Also note that separating the device programming and logic simplifies porting of the code to a different hardware platform.
This pattern can be used to represent any hardware device.
The structure of class in this design pattern largely depends upon the register programming model of the device being programmed. In most cases, this design pattern would be implemented as a single class representing the device. In case of complex devices, the device might be modeled as a main device class and other subclasses modeling different parts of the device.
This design pattern generally interfaces with other classes that need to access hardware registers.
Collaboration between the device class and other classes would largely depend upon the purpose for which the device is being used. For example, a hardware device that provides timers, serial ports and a DMA controller might be modeled just as a serial device if other device functionality is not being used.
The device involved in low level hardware programming is simplified as details about register manipulation have been hidden within the class. Thus the code accessing the hardware device can focus on the logic of the operation being performed. As noted earlier, porting of hardware dependent software is also simplified.
We will study the implementation of this pattern by working with an imaginary serial device with the following register set:
- Status Register (STAT): This read only register contains the following
- Bit 0: Transmit Buffer Has Empty Space
- Bit 1: Receive Buffer Has Data
- Bit 2: Transmit under run
- Bit 3: Receive overrun
- Action Register (ACT): Bits in this write only register correspond to the bits in the status register. A condition in the status register can be cleared by writing the corresponding bit as 1. Note that bit 0 automatically gets cleared when writes are performed to the transmit buffer. Bit 1 is cleared automatically when reads are performed from the receive buffer. Bit 2 and 3 however need to be cleared explicitly.
- Transmit Buffer (TXBUF): Write only buffer in which bytes meant for transmission should be written.
- Receive Buffer (RXBUF): Read only buffer in which received bytes are stored.
Sample Code and Usage
Here is the code for the Serial_Device class:
This pattern is used to decouple the logical device handling and device register manipulation.