Devil: A DEVice Interface Language

Devil (DEVice Interface Language) is an IDL aimed at providing the lower layer of a device driver, i.e., the basic interaction with the device. A Devil specification rigorously describes the access mechanisms, the type and the layout of data that are exchanged to operate the device, as well as some behavioral properties. It does not assume any particular OS, and can therefore be used for any target platform.

 


How to get Devil?

The present distribution contains the Devil compiler (named taz) as a binary and a set of specifications for testing it.
Please note that this release is an alpha version. There are many known limitations.

NEW Source code of the Taz compiler released under LGPL.

 


Related Papers

2001

Reports

titre
Dealing with Hardware in Embedded Software: A Retargetable Framework Based on the Devil Language
auteur
Fabrice Mérillon, Gilles Muller
article
[Research Report] RR-4187, INRIA. 2001
Accès au texte intégral et bibtex
https://hal.inria.fr/inria-00072436/file/RR-4187.pdf BibTex
titre
Improving Driver Robustness : an Evaluation of the Devil Approach
auteur
Laurent Réveillère, Gilles Muller
article
[Research Report] RR-4136, INRIA. 2001
Accès au texte intégral et bibtex
https://hal.inria.fr/inria-00072490/file/RR-4136.pdf BibTex

2000

Conference papers

titre
A DSL approach to improve productivity and safety in device drivers development
auteur
Laurent Réveillère, Fabrice Mérillon, Charles Consel, Renaud Marlet, Gilles Muller
article
15th IEEE International Conference on Automated Software Engineering, 2000, France. pp.101-109, 2000
Accès au texte intégral et bibtex
https://hal.archives-ouvertes.fr/hal-00350233/file/ase00-devil.pdf BibTex
titre
Devil : An IDL for Hardware Programming
auteur
Fabrice Mérillon, Laurent Réveillère, Charles Consel, Renaud Marlet, Gilles Muller
article
Symposium on Operating Systems Design and Implementation, 2000, United States. pp.17-30, 2000
Accès au texte intégral et bibtex
https://hal.archives-ouvertes.fr/hal-00350223/file/osdi00-merillon.pdf BibTex
titre
Towards robust OSes for appliances: A new approach based on Domain-Specific Languages
auteur
Gilles Muller, Charles Consel, Renaud Marlet, L. P. Barreto, Fabrice Mérillon, Laurent Réveillère
article
SIGOPS European Workshop, 2000, France. 2000
Accès au texte intégral et bibtex
https://hal.archives-ouvertes.fr/hal-00350228/file/PI-1327.pdf BibTex

Reference manual

 


How to use the taz compiler?

The taz compiler takes a Devil specification as an input and generates a C interface that implements this specification.
The following options can be used in the command line :
-o filename Set the name of the output file (defaults to inputname.h where inputname is the name of the input file)
-nocode Do not generate code, only verifications are performed
-version Display version information

 


How to use the generated C interface?

Suppose that we have the following Devil specification :

device foo (base : bit[8] port @{0..1})
{
register cmd = write base@0, mask '1011 0...' : bit[8];
register stat = read base@1, mask '**** **..' : bit[8];

variable command= cmd[2..0] : {
ON => '100',
OFF => '000',
BLINK => '101'
};

structure status = {
variable ready = stat[1], volatile : bool;
variable died = stat[0], volatile : bool;
};
}


Once compiled, a C interface is produced from this specification. The following types, variables, and functions are defined :

  • Device : the type dev_foo is created. The driver must declare a variable of this type and call the function init_foo(base) to specify the value of each parameter of the Devil specification;
    • void     init_foo(dev_foo *dev, t1 param1, ...)
    • dev_foo *new_foo(int priority, t1 param1, ...) where priority is the priority to use for the kernel malloc function
  • Boolean : the boolean type bool is declared and have values true and false;
  • Types : A type bar_t is declared for each type bar defined in the specification. For anonymous types, the name used is the concatenation of the variable name (on which the type was attached) and the string "_t";
  • Variables : For each variable xxx, of type ttt, defined in the specification, two functions get_xxx and set_xxx are created with the following prototypes :
    • ttt  get_xxx(dev_foo *dev)
    • void set_xxx(dev_foo *dev, ttt val)
      • an update of a cache if the variable is a structure field (no physical read or write)
      • a read or write to the registers that map xxx otherwise.
  • Note that a call to get_xxx or set_xxx induces
  • Structures : For each structure S, defined in the specification, two functions get_S and set_S are created with the following prototypes :
    • void get_S(dev_foo *dev)
    • void set_S(dev_foo *dev)
  • Note that a call to get_S or set_S induces a read or write of all variables grouped inside the structure (physical read or write).
  • Common functions : Some functions are generated in order to manipulate Devil types and values in C.
    • int dil_eq(t1 x, t1 y) where t1 is a Devil type, returns 1 iff x = y, 0 otherwise.
    • DIL_SWITCH(x) : macro to use instead of "switch x" when x has a Devil type
    • DIL_CASE(x)   : macro to use instead of "case x" when x has a Devil type

The produced interface can be used in two differents modes, with references or without.

  1. With reference : (default) In this mode, all functions need a pointer to a device structure as a parameter. This mode is often used when the driver has to manipulate several devices. Here, is an example for using the C interface :
      1. #include "foo.dil.h"
  2. Without reference : In this mode, the device structure is automatically allocated once, the new function is not created and each function is prefixed by the name of the device. This name can be modified with the dev_name flag. Functions get and set becomes :
      1. #define DEVIL_NO_REF
        #define dev_name foo1
        #include "foo.dil.h"
    • ttt  get_xxx() and void set_xxx(ttt val) for variables
    • void get_S() and void set_S() for structures
  3. Here is an example for using the C interface in this mode and naming the targeted device as foo1


For efficiency purpose, the generated interface can be used with 3 different levels of type safety

  • level 0 : (default) Types are represented with C native types, without any overhead. No run-time checks are performed
  • level 1 : All Devil types (excepted bool) are fresh C types. The cost penalty is non nul but really weak.  Code is inserted to enable run-time verifications. The DEVIL_DEBUG flag must be set in order to select this type safety level.
      • #define DEVIL_DEBUG
        ... /* depends on the mode used REF/NO_REF */
        #include "foo.dil.h"
  • level 2 : The same level that level 1, plus the fact that the bool type is also a fresh type. The driver cannot consider a boolean value as an integer. To select this mode, set the DEVIL_BOOL_TYPED flag. The DEVIL_DEBUG flag must be set for this mode. Here is an example of using this mode.
      • #define DEVIL_DEBUG
        #define DEVIL_BOOL_TYPED
        ... /* depends on the mode used REF/NO_REF */
        #include "foo.dil.h"


Real driver performances


In order to evaluate the benefit and impact of Devil in driver development, we are currently re-engineering various Linux drivers which run on recent bi-processor PCs installed in our group.1

IDE Driver

Table 1 presents a performance comparison of a Devil-based IDE driver against the original C driver. Throughput measurements were obtained using the standard Linux hdparm utility. Two Devil specifications have been written for this driver: a specification of the IDE controller and a specification of the Intel PIIX4 PCI busmater IDE.

We have run the IDE driver in both Ultra DMA-2 and several PIO modes, varying the size of I/O (16 or 32 bits) and the number of sectors transfered per interrupts. In DMA mode, Devil induces 6 additional I/O operations to prepare the command. Because of the duration of the DMA transfer, there is no impact on the available throughput. In PIO modes, there are 3 additional I/O operations to prepare the command, plus 2 for each interrupt (#s denotes the total number of sectors of the access). On specific processors such as those of the Pentium family, replacing a C loop over a variable read/write by a dedicated looping instruction (e.g., rep) is often more efficient. This situation can be found for the Programmed I/O (PIO) transfer mode of the IDE Linux driver; using a C loop induces a 10% throughput penalty.

Standard driver Devil driver
Transfer
mode
Sectors
per
interrupt
I/O
Size
in bits
I/O
Operations
Throughput
in Mb/s
I/O
Operations
Throughput
in Mb/s
Devil/Stand.
throughput
ratio
DMA - - 14 14.25 20 14.25 100 %
PIO 16 32 7+#s(1+128)/16 8.17 10+#s(3+128)/16 7.36 90 %
16 7+#s(1+256)/16 4.45 10+#s(3+256)/16 3.94 88 %
8 32 7+#s(1+128)/8 8.09 10+#s(3+128)/8 7.28 89 %
16 7+#s(1+256)/8 4.42 10+#s(3+256)/8 3.91 88 %
1 32 7+#s(1+128) 6.93 10+#s(3+128) 6.36 91 %
16 7+#s(1+256) 4.06 10+#s(3+256) 3.63 89 %

Table 1: IDE Linux driver comparative performance results

X11 Driver

Tables 2.1 and 2.2 present a performance comparison of a Devil-based X11 driver against the original C driver. Throughput measurements were obtained using the xbench utility.

An X11 driver is a module which is built into an X11 server. Unlike most Linux drivers, an X11 driver is therefore not a kernel module : it runs in user-mode. In our experiment, we used the 3DLabs-dedicated server from the popular Xfree86 implementation of X11 (version 3.3.6). This server includes a driver for 3DLabs' Permedia2 graphics controller. Although this chip provides acceleration for both 2D and 3D, the X11 server does not take advantage of 3D. Moreover, to minimize hardware-dependant code, the server implements many 2D primitives in software. Hardware acceleration is only used for the two most time-consuming primitives : filled rectangle and screen area copy. Tables below present results for both of these accelerated primitives.

The number of drawn pixels and the depth (number of bits) of each pixel influence the completion time of a primitive. Indeed, the higher these figures are, the more video RAM is accessed, and the busier the graphics controller is. Since the CPU is only used for triggering the controller, driver optimization is more important when primitive calls are light and quick. We therefore conducted tests in various contexts, and the slight CPU overhead introduced by Devil showed only for the lightest calls. For primitive calls involving at least 100 pixels (which are the most common in pratice), 99% to 100% of the speed were retained (always 100% at 24 bits, the most common depth). For the tiny 4-pixel calls, results ranged 94%-100% (97%-100% at 24 bits). We therefore can conclude that using Devil in this driver did not introduce any significant performance penalty.

Note : Due to the programming model of Permedia2, the driver must wait (busy loop) for free entries in an on-chip FIFO before accessing the chip. Such loops take one I/O per iteration. In the tables, the number of I/O operations is given with a w variable, which is the average number of I/Os per waiting loop. For example, 3w + 15 means that the driver performs 3 waiting loops, plus 15 useful I/Os.

 

Standard Driver Devil Driver
Display Mode
(bits/pixel)
Rectangle Size
(pixels)
I/O
Operations
Troughput
(rectangles/s)
I/O
Operations
Troughput
(rectangles/s)
Devil/Standard
Troughput Ratio
8 2 x 2 3w + 15 984838 3w + 17 949052 96 %
10 x 10 589621 585350 99 %
100 x 100 38472 38438 100 %
400 x 400 3762 3762 100 %
16 2 x 2 3w + 15 982338 3w + 17 945916 96 %
10 x 10 333670 332499 100 %
100 x 100 21022 21033 100 %
400 x 400 2221 2221 100 %
24 2 x 2 2w + 10 978605 2w + 10 945884 97 %
10 x 10 235119 234716 100 %
100 x 100 3693 3693 100 %
400 x 400 244 243 100 %
32 2 x 2 3w + 15 957534 3w + 17 929833 97 %
10 x 10 251522 251584 100 %
100 x 100 10466 10466 100 %
400 x 400 899 899 100 %

Table 2.1 : Comparative Performance of Permedia2 Xfree86 Driver : Rectangle Test

 

Standard Driver Devil Driver
Display Mode
(bits/pixel)
Copy Size
(pixels)
I/O
Operations
Troughput
(copies/s)
I/O
Operations
Troughput
(copies/s)
Devil/Standard
Troughput Ratio
8 2 x 2 3w + 15 149553 3w + 17 144494 97 %
10 x 10 123584 122300 99 %
100 x 100 10662 10638 100 %
400 x 400 764 764 100 %
16 2 x 2 3w + 15 145084 3w + 17 136755 94 %
10 x 10 85994 85561 99 %
100 x 100 3502 3512 100 %
400 x 400 238 238 100 %
24 2 x 2 2w + 9 144385 2w + 9 144521 100 %
10 x 10 77443 77605 100 %
100 x 100 1716 1716 100 %
400 x 400 114 114 100 %
32 2 x 2 2w + 9 142335 2w + 9 142598 100 %
10 x 10 69762 69804 100 %
100 x 100 1703 1701 100 %
400 x 400 111 111 100 %

Table 2.2 : Comparative Performance of Permedia2 Xfree86 Driver : Screen Copy Test

 


1

The PC is a DELL Precision 210 with the following configuration: two Pentium II 450 MHz, Intel PIIX4 PCI chipset, Maxtor model 91000D8 UDMA2 19.5Gb disk (512Kb cache), 3DLabs Permedia2 Graphics Controller.