Project

General

Profile

Driver Creation Guidelines

This section outlines some guidelines for creating drivers for FPGA Core or OMAP native devices for the ARM and the DSP. These guidelines are in place to aid in creating a more uniform code base and in hopes that common mistakes can be more easily avoided.

The Header File and Control Register Access

Every device driver should begin with a header file that defines the locations of the various registers as well as the bit definitions for each registers.

Register Offsets

First, the register offsets should be defined as offsets from a base address for the given device. In this way if there are multiple devices or the device address is not static, as in the case of FPGA cores, only the base address needs to be changed. Also, the offset width (i.e. the logical size of each control register) should be specified. The idea is that the driver code will store a base address pointer and use the offsets to access the various control registers.

Here are examples of offsets to be included in the header file:

/* Register Word (2 byte) Offsets */
const int gnVER_OFFSET = 0; //!< Version Register.
const int gnCTRL_OFFSET = 1; //!< Control Register.
const int gnRX_FIFO_OFFSET = 2; //!< Receive FIFO Register.

It is then assumed the driver class will have a base address pointer such as:

volatile uint16_t* mpBaseAddress; 

The driver code can then access the register in the following manner:

// Clear the control register
mpBaseAddress[gnCTRL_OFFSET] = 0;

// Read the RX FIFO
uint16_t val = mpBaseAddress[gnRX_FIFO_OFFSET];

Note that for FPGA Cores the register at offset 0 is always the Version Register as this is used to identify the core and provide any necessary information.

Register Bit Definitions

In many cases control registers are divided into multiple bit fields which affect various parts of the unit. In order to aid in accessing these, but still ensure proper reads and writes, a union similar to the following is used for bit definitions:

typedef union
{
        struct
        {
                uint16_t tx_go : 1; //!< Transmit 'go' bit. Starts transmit.
                uint16_t tx_pol : 1; //!< Transmit data polarity. 0 = active high.
                uint16_t unused_0 : 2;          
                uint16_t rx_pol : 1; //!< Receive input polarity. 0 = active high.
                uint16_t unused_1 : 10;
                uint16_t srst : 1; //!< Synchronous reset.
        } msBits;
        uint16_t mnWord;
} tuCtrlReg;

Here is an example of a proper read-modify-write for setting the tx_pol in the above register:

tuCtrlReg luCtrlReg;

// Read current register value
luCtrlReg.mnWord = mpBaseAddress[gnCTRL_OFFSET];
// Set transmit polarity to active low
luCtrlReg.msBits.tx_pol = 1;
// Write back modified value of register
mpBaseAddress[gnCTRL_OFFSET] = luCtrlReg.mnWord;

Examples

See the MDK for some complete header files.
  • See $MDK/sw/common/fpga for FPGA Core header file examples.

Go to top
Add picture from clipboard (Maximum size: 1 GB)