Professional Documents
Culture Documents
DPS Tutorial PDF
DPS Tutorial PDF
Version: 1.0
1 Overview
This document explains how to write a DPS (Device Programming Specification)
file, a formal representation of the hardware IP. DPS has been specifically devised
for DDGEN (Device Driver Generator) so that it can be used to generate code in C
to program the hardware.
DDGEN is a tool that generates device drivers automatically if provided with two
inputs:
• The DPS file.
• The run time environment specified in a configuration file called as the
Run Time Specification file (also referred as RTS file).
DDGEN can be run from the command line or from the Eclipse IDE by using the
DDGEN plug-in.
2 Typographical Conventions
Symbol/Meta charaters Meaning
Courier 11 • DPS keywords and characters that are part of
the DPS syntax.
• Code snippets, or reference to code snippets.
• Literals
The partially filled files can be completed with help of auto-completion (short cut
key: CTRL+SPACE). Syntax highlighting, mouse hover and outline view can
help the user in completing the DPS files correctly.
! Right click on the project select Import. Import window will appear
! Select File System and click Next and browse the folder path where
the file is present, Click on OK and select the file that needs to be
imported.
To help the user in editing the DPS files, the DDGen Eclipse plugin provides
auto-completion, syntax highlighting, and mouse hover. Please refer to the
document “DDGen Eclipse Plugin User Manual.pdf” for more details.
Applying the DPS register field format to RBR’s register bits, the register
field is defined as follows:
field RBR <0:7> {
description = “Received data”;
sw_axs = ro;
clearing_mode = dc;
}
Combining the RBR’s register with field specification, the complete register
definition of RBR in DPS is:
RBR [8] @ 0 {
description = “Receiver Buffer Register”;
sw_axs = ro;
}
}
• register bit 4 is related to even parity select (EPS), hence bit 4 is one field.
• register bit 5 is related to stick parity (SP), hence bit 5 is one field.
• register bit 6 is related to set break (SB), hence bit 6 is one field.
• register bit 7 is related to divisor latch access bit (DLAB), hence bit 7 is
one field.
Using the above information LCR can be specified in DPS in the following
manner:
LCR [8] @ 3 {
description = “Line Control Register”;
sw_axs = rw;
Alternately LCR can be specified in DPS with its bits 3, 4 and 5 clubbed as one
field that controls the parity. This is entirely left to the discretion of the driver
writer on the granularity of control required. The LCR definition would look
like this:
LCR [8] @ 3 {
description = “Line Control Register”;
sw_axs = rw;
}
}
}
Notes on what can be specified on the right hand side of each of the device
specification fields:
• DEVICE_NAME: Should be a sequence characters that can ONLY be
alphanumeric or underscore. Starting character to be an alphabet.
• MANUFACTURER_NAME: Should be the manufacturer’s name enclosed in
double quotes.
• DEVICE_VERSION: Should describe the device version, or a device
version number enclosed in double quotes.
• DEVICE_CLASS: Should be one of the DPS keywords specifying the type
of device that best describes the device under consideration. The valid
keywords that can be used are the following:
o SERIAL
o MEMORY
o NETWORK
o USB_DEVICE
o USB_HOST
o AUDIO
o VIDEO
o DMA_CONTROLLER
o I2C_MASTER
o SPI_CONTROLLER
o MEMORY_CONTROLLER
o STANDARD_DATA_CONTROL
o VIDEO_CAPTURE
o INTERNAL_SUB_DEV
o VIDEO_BUFFER
o VIDEO_DECODER
o AUDIO_INTERFACE
o INPUT_DEVICE_CONTROLLER
o DEFAULT
Please use DEFAULT for all other device classes to generate bare metal
drivers.
• DEVICE_INPUT_CLOCK: Could be a frequency or a set/range of
frequencies that specify the input clock frequencies to the device. This
field is optional. If the DEVICE_INPUT_CLOCK specifies multiple values,
and a single value required to be used in the driver as part of DPS sequence, then
the required value will have be picked up from the “run time environment file”
(the RTS file ) given as the second input to DDGEN.
4.4 Sequences
The DPS sequences are the DPS equivalent of a device driver API that programs
the device for a specific functionality.
Based on the data sheet the formula co-relating the baud rate and the 16-
bit divisor is:
16-bit divisor = input clock frequency / (baud rate * 16)
2. configure the word length, stop bit, parity for the data transmission
Configuring the word length, stop bit and parity are related to the
programming of the LCR register. The snippet of the data sheet related to
describing these bits in LCR of PC16550D is as follows:
How to do it:
As a good programming practice it is important to have defensive
programming in place so that invalid values passed to the function are
handled gracefully. It is important to decide on what is expected as the
valid values for each of the parameters based on its functionality. It should
be meaningful at the application level and at the same time keep the code
size of the driver as small as possible. For example:
• Word length as per the datasheet can be 5, 6, 7 or 8, where as the
values to be set in the register field are 0, 1, 2 or 3. So if the input
parameter word_length can be an enum descriptive of the
functionality while its value is what needs to programmed in the
register field, then it would satisfy both the conditions without any
coding overhead.
• Stop bit as per datasheet can be 1, 1.5 or 2, but this is also dependent
on the data length. If data length is 5 bits, then the stop bit can be 1 or
1.5, but not 2. However if data length is 6, 7 or 8, then the stop bit can
be 1 or 2. To avoid confusion we can have enums like short_stopbit
and long_stopbit, which means 1 stop bit, or 1.5/2 stop bits. These
enums can be assigned values that are required to program the bit 2 of
LCR to set the stop bit length accordingly.
• Even/odd parity can be set by programming bit 4 of LCR to 1/0
accordingly. So if we use enums like even_parity = 1 and odd_parity =
0, that can be passed as the parity parameter, then the value can be
used to program the LCR directly.
LCR.WLS = word_length;
LCR.STB = stop_bit;
# Enable parity before setting even, or odd parity
LCR.PEN = 0x1;
LCR.EPS = parity;
}
3. Receive data
The data received by the UART PC16550D is stored in a FIFO. The FIFO
can store up to 16 bytes of data, but the access to the FIFO is one byte at a
time through the register RBR.
What needs to be done to implement the feature:
Read RBR and store it in the memory pointed to by character pointer buff
How to do it:
The implementation would be:
• Wait until bit 0 of LSR is 1 indicating that data is available in the Receiver
buffer Register (RBR)
• Once data is available, check if any errors like: parity, overrun, framing or
break error has occurred.
• If no errors have occurred, then read RBR register until bit 0 of LSR is 0.
4. Transmit data
The data to be transmitted by the UART PC16550D is the data written into
xmitfifo(cnt) = buf;
}
buf = rcvfifo(cnt);
}
PC16550D supports the interrupts shown by the following snippet from the data
sheet:
rx_data_av {
status = IIR.INT_ID(2);
int_type = device_read;
enable_field = IER.ERBFI(0x1);
disable_field = IER.ERBFI(0x0);
clear_field = auto_clear;
interrupt_priority = 1;
}
rx_line_stat {
status = IIR.INT_ID(3);
int_type = error;
enable_field = IER.ELSI(0x1);
disable_field = IER.ELSI(0x0);
clear_field = cor LSR;
interrupt_priority = 0;
parity_error {
status = LSR.PE(1);
int_type = error;
clear_field = cor LSR;
interrupt_priority = 0;
}
overrun_error {
status = LSR.OE(1);
int_type = error;
clear_field = cor LSR;
interrupt_priority = 0;
}
frame_error {
status = LSR.FE(1);
int_type = error;
clear_field = cor LSR;
interrupt_priority = 0;
}
break_error {
status = LSR.BI(1);
int_type = error;
clear_field = cor LSR;
interrupt_priority = 0;
}
}
}
}
Explanation:
• PC16550D supports only one interrupt line and this is captured under an
identifier uart{…}. If the device supported multiple interrupt lines, then
there would be multiple specifications like uart{…} under different unique
identifiers within interrupt_spec.
! Writing the specified value into the register field. This would be cow
register.field(value). cow means “clear on write”.
! auto_clear: this would mean “nothing specific to be done”.
o interrupt_priority indicates the priority of the interrupt.
The DPS constructs can span across multiple files. Typically the register definitions
are specified in a separate DPS file, while the remaining DPS constructs are written
in different DPS file.
DDGEN does not impose any restrictions on the number of files or how the files
need to be named, but all the DPS files available to the user follow the convention
of naming the main DPS file based on the IP and the register definition file as ip-
name_reg_def.dps. For example in case of PC16550D, the main DPS file can be
called pc16550d.dps and the file containing the register definitions can be
pc16550d_reg_def.dps.
In order for DDGEN to pick up all the DPS files any one of the following
approaches can be used:
1. On command line:
• Specify all the DPS files separated by a space on the command line following the
–E option. Example:
• #include the other DPS files the main DPS file. E.g In pc16550d.dps specify:
• Place all the DPS files in in the project folder in order for it to be picked up by
DDGEN.
APPENDIX A – pc16550d_reg_def.dps
This is the DPS file containing the register definitions of PC16550D – Universal
Asynchronous Receiver/Transmitter.
/*
* This is the DPS file for PC16650D Universal Asynchronous
* Transmitter/Receiver with FIFOs
*/
DATA_REGISTERS {
RBR [8] @ 0 {
SW_AXS = RO;
DESCRIPTION = "RECEIVER BUFFER REGISTER";
FIELD RBR <0:7> {
SW_AXS = RO;
DESCRIPTION = "This register holds the receives data";
CLEARING_MODE = DC;
VALUE_ON_RESET = 0;
}
}
THR [8] @ 0 {
SW_AXS = WO;
DESCRIPTION = "TRANSMITTER HOLDING REGISTER";
FIELD THR <0:7> {
SW_AXS = WO;
DESCRIPTION = "This register holds the data to be transmitted";
CLEARING_MODE = DC;
VALUE_ON_RESET = 0;
}
}
}
INTERRUPT_REGISTERS {
IER [8] @ 1 {
SW_AXS = RW;
DESCRIPTION = " INTERRUPT ENABLE REGISTER";
FIELD ERBFI <0:0> {
SW_AXS = RW;
DESCRIPTION = "Enable Receive Data Available Interrupt";
CLEARING_MODE = DC;
VALUE_ON_RESET = 0;
}
FIELD ETBEI <1:1> {
SW_AXS = RW;
DESCRIPTION = "Enable Transmit Holding Register Empty Interrupt";
CLEARING_MODE = DC;
VALUE_ON_RESET = 0;
}
FIELD ELSI <2:2> {
SW_AXS = RW;
DESCRIPTION = "Enable Receive Line Status Interrupt";
CLEARING_MODE = DC;
VALUE_ON_RESET = 0;
}
FIELD EDSSI <3:3> {
SW_AXS = RW;
DESCRIPTION = "Enable Modem Status Interrupt";
CLEARING_MODE = DC;
VALUE_ON_RESET = 0;
}
RESERVED <4:7> = 0;
}
IIR[8] @ 2 {
SW_AXS = RO;
}
FIELD PEN <3:3> {
SW_AXS = RW;
DESCRIPTION = "PARITY ENABLE BIT\
The Parity bit is used to produce an even or odd number \
of 1s when the data word bits and the Parity bit are \
summed. When bit 3 is a logic 1, a Parity bit is \
generated or checked between the last data word bit \
and Stop bit of the serial data.";
CLEARING_MODE = DC;
VALUE_ON_RESET = 0;
}
FIELD EPS <4:4> {
SW_AXS = RW;
DESCRIPTION = "EVEN PARITY Select \
When bit 3 is a logic 1 and bit 4 is a logic 0, an odd \
number of logic 1s is transmitted or checked in the \
or checked in the data word bits and Parity bit. When \
bit 3 is a logic 1 and bit 4 is a logic 1, an even \
number of logic 1s is transmitted or checked";
CLEARING_MODE = DC;
VALUE_ON_RESET = 0;
ENUM {
even_parity = 1;
odd_parity = 0;
}
}
FIELD STKPAR <5:5> {
SW_AXS = RW;
DESCRIPTION = "STICKY PARITY Bit\
When set to logic 0 Stick Parity disable\
When bits 3, 4 and 5 are logic 1 the Parity bit is \
transmitted and checked as a logic 0. If bits 3 and 5 \
are 1 and bit 4 is a logic 0 then the Parity bit is \
transmitted and checked as a logic 1.";
CLEARING_MODE = DC;
VALUE_ON_RESET = 0;
ENUM {
disable_sticky_par = 0;
enable_sticky_par = 1;
}
}
FIELD SETBRK <6:6> {
SW_AXS = RW;
DESCRIPTION = "Break Control Bit\
When set to logic 0 : break control is disabled\
It causes a break condition to be transmitted to the \
receiving UART. When it is set to a logic 1, the \
serial output (SOUT) is forced to the Spacing \
(logic 0) state.";
CLEARING_MODE = DC;
VALUE_ON_RESET = 0;
}
FIELD DLAB <7:7> {
SW_AXS = RW;
DESCRIPTION = "Divisor Latch Access Bit\
Set to logic high to access divisor latches of the \
baud generator during R/W. Set logic low to access Rx\
buffer ,THR, IER";
CLEARING_MODE = DC;
VALUE_ON_RESET = 0;
}
}
}
STATUS_REGISTERS {
LSR [8] @ 5 {
SW_AXS = RW;
DESCRIPTION = " LINE STATUS REGISTERS";
FIELD DR <0:0> {
SW_AXS = RW;
DESCRIPTION = "Date Ready Indicator";
CLEARING_MODE = COR RBR;
VALUE_ON_RESET = 0;
}
FIELD OE <1:1> {
SW_AXS = RW;
DESCRIPTION = "Over Run Error received";
CLEARING_MODE = COR LSR;
VALUE_ON_RESET = 0;
}
FIELD PE <2:2> {
SW_AXS = RW;
DESCRIPTION = "Parity Error received";
CLEARING_MODE = COR LSR;
VALUE_ON_RESET = 0;
ENUM {
enabled = 0x1;
disabled = 0x0;
}
}
FIELD FE <3:3> {
SW_AXS = RW;
DESCRIPTION = "Framing Error received";
CLEARING_MODE = COR LSR;
VALUE_ON_RESET = 0;
}
FIELD BI <4:4> {
SW_AXS = RW;
DESCRIPTION = "Break Interrupt received";
CLEARING_MODE = COR LSR;
VALUE_ON_RESET = 0;
}
FIELD THRE <5:5> {
SW_AXS = RW;
DESCRIPTION = "Transmitter Holding Register Empty";
CLEARING_MODE = DC;
VALUE_ON_RESET = 1;
}
FIELD TEMT <6:6> {
SW_AXS = RW;
DESCRIPTION = "Transmitter holding and shift register empty";
CLEARING_MODE = DC;
VALUE_ON_RESET = 1;
}
FIELD ERXFIFO <7:7> {
SW_AXS = RW;
DESCRIPTION = "Error In Receiver FIFO";
CLEARING_MODE = COR LSR;
VALUE_ON_RESET = 0;
}
}
}
GENERAL_PURPOSE_REGISTERS {
DLL [8] @ 0 {
SW_AXS = RW;
DESCRIPTION = "DEVICE LATCH LEAST SIGNIFICANT BYTE";
FIELD DLL <0:7> {
SW_AXS = RW;
CLEARING_MODE = DC;
VALUE_ON_RESET = NA;
}
}
DLM [8] @ 1 {
SW_AXS = RW;
DESCRIPTION = "DEVICE LATCH MOST SIGNIFICANT BYTE";
APPENDIX B – pc16550d.dps
This is the DPS file containing the remaining specifications other than the register
definitions of PC16550D – Universal Asynchronous Receiver/Transmitter.
device_spec {
device_name = pc16550d;
manufacturer_name = "National Semiconductors";
device_version = "Improved cersion of 16450";
device_class = serial;
device_input_clock = 0Hz - 24MHz ;
}
bus_spec {
reg_access_type = memory_mapped;
}
memory_spec {
type = fifo;
fifo xmitfifo {
type = HW_FIFO;
transfer_type = transmit;
max_depth = 16;
max_wordsize = 1;
access_mechanism = THR.THR;
}
fifo rcvfifo {
type = hw_fifo;
transfer_type = receive;
max_depth = 16;
max_wordsize = 1;
access_mechanism = RBR.RBR;
}
}
LCR.WLS = word_length;
LCR.STB = stop_bit;
LCR.PEN = 0x1;
LCR.EPS = parity;
}
description = "This function configures the baud rate for data transmission and
reception";
sequence configure_baud_rate
{
input baud (50,75,110,9600,128000);
local unsigned int divisor;
description = "This function reads the data from the receive FIFO";
sequence device_read {
output unsigned char buf[];
input unsigned int cnt;
description = "This function write the data into the transmit FIFO";
sequence device_write {
input unsigned char buf[];
input unsigned int cnt;
if (LSR.THRE == 0x1) {
xmitfifo(cnt) = buf;
}
}
interrupt_spec {
interrupt_pending = IIR.INT_PEND(0x0) ;
uart {
tx_reg_empty {
status = IIR.INT_ID(1);
int_type = device_write ;
enable_field = IER.ETBEI(0x1) ;
disable_field = IER.ETBEI(0x0) ;
clear_field = auto_clear ;
interrupt_priority = 2;
}
time_out {
status = IIR.INT_ID (6);
int_type = error;
clear_field = auto_clear;
enable_field = IER.ERBFI (0x1);
disable_field = IER.ERBFI (0x0);
interrupt_priority = 1;
}
rx_data_av {
status = IIR.INT_ID (2);
int_type = device_read;
enable_field = IER.ERBFI (0x1);
disable_field = IER.ERBFI (0x0);
clear_field = auto_clear;
interrupt_priority = 1;
}
rx_line_stat {
status = IIR.INT_ID(3);
int_type = error;
enable_field = IER.ELSI (0x1);
disable_field = IER.ELSI (0x0);
clear_field = cor LSR;
interrupt_priority = 0;
parity_error {
status = LSR.PE (1);
int_type = error;
clear_field = cor LSR;
interrupt_priority = 0;
}
overrun_error {
status = LSR.OE (1);
int_type = error;
clear_field = cor LSR;
interrupt_priority = 0;
}
frame_error {
status = LSR.FE (1);
int_type = error;
clear_field = cor LSR;
interrupt_priority = 0;
}
break_error {
status = LSR.BI (1);
int_type = error;
clear_field = cor LSR;
interrupt_priority = 0;
}
}
}
}