Writing a HAL component can be a tedious process, most of it in setup
calls to rtapi_ and hal_ functions and associated
error checking. comp will write all this code for you, automatically.
Compiling a realtime HAL component is also much easier when using
comp, whether the component is part of the emc2 source tree,
or outside it.
For instance, the ``ddt'' portion of blocks is around
80 lines of code. The equivalent component is very short when written
using the comp preprocessor:
-
- component ddt ``Compute the derivative of the input function'';
pin in float in;
pin out float out;
option data float;
function _;
;;
MODULE_LICENSE("GPL");
FUNCTION(_) {
float tmp = in;
out = (tmp - data) / fperiod;
data = tmp;
}
and it can be compiled and installed very easily: by simply placing
ddt.comp in src/hal/components and running 'make',
or by placing it anywhere on the system and running comp -install
ddt.comp
- component
- A component is a single real-time module, which is
loaded with halcmd loadrt. One .comp file specifies
one component.
- instance
- A component can have zero or more instances. Each instance
of a component is created equal (they all have the same pins, parameters,
functions, and data) but behave independently when their pins, parameters,
and data have different values.
- singleton
- It is possible for a component to be a 'singleton',
in which case exactly one instance is created. It seldom makes sense
to write a singleton component, unless there can literally
only be a single object of that kind in the system (for instance,
a component whose purpose is to provide a pin with the current UNIX
time, or a hardware driver for the internal PC speaker)
For a singleton, the one instance is created when the component is
loaded.
For a non-singleton, the 'count' module parameter determines
how many numbered instances are created.
A .comp file consists of a number of declarations, followed
by ;; on a line of its own, followed by C code implementing
the module's realtime functions.
Declarations include:
-
- component NAME [DOC];
pin PINDIRECTION TYPE NAME [DOC];
param PARAMDIRECTION TYPE NAME [= STARTVALUE] [DOC] ;
function NAME [fp | nofp] [DOC];
option NAME [VALUE];
Brackets indicate optional items. A vertical bar indicates alternatives.
Words in CAPITALS indicate variable text, as follows:
- NAME
- A C identifier.
When used to create a HAL identifier, any underscores are replaced
with dashes, and any trailing dash is removed, so that ``this_name_''
will be turned into ``this-name''. If the name is ``_'',
then a trailing period is removed as well, so that ``function
_'' gives a HAL function name like component.<num> instead
of component.<num>.
If present, the prefix hal_ is removed from the beginning
of the component name when creating pins, parameters and functions.
- DOC
- A string that documents the item. String can be a C-style
``double quoted'' string, like "Selects the desired
edge: TRUE means falling, FALSE means rising" or a Python-style
``triple quoted'' string, which may include embedded newlines
and quote characters, such as:
-
- param rw bit zot=TRUE
"""The effect of this parameter, also known as "the orb of zot",
will require at least two paragraphs to explain.
Hopefully these paragraphs have allowed you to understand "zot"
better.""";
- TYPE
- One of the HAL types: bit, s32, u32,
or float.
- PINDIRECTION
- One of the following: in, out,
or io. A component sets a value for an out pin,
it reads a value from an in pin, and it may read or set the
value of an io pin.
- PARAMDIRECTION
- One of the following: r or rw.
A component sets a value for a r parameter, and it may read
or set the value of a rw parameter.
- STARTVALUE
- Specifies the initial value of a parameter. If it
is not specified, then the default is 0 or FALSE,
depending on the type of the parameter.
- fp
- Indicates that the function performs floating-point calculations.
- nofp
- Indicates that it only performs integer calculations. If
neither is specified, fp is assumed. Neither comp nor gcc
can detect the use of floating-point calculations in functions that
are tagged nofp.
- VALUE
- Depending on the option name, the valid VALUEs vary. The
currently defined options are:
- option singleton yes
- (default: no)
Do not create a count module parameter, and always create
a single instance. With singleton, items are named component-name.item-name
and without singleton, items for numbered instances are named
component-name.<num>.item-name.
- option default_count
- (default: 1)
Normally, the module parameter count defaults to 0. If specified,
the count will default to this value instead.
- option count_function yes
- (default: no)
Normally, the number of instances to create is specified in the module
parameter count; if count_function is specified,
the value returned by the function int get_count(void) is
used instead, and the count module parameter is not defined.
- option rtapi_app no
- (default: yes)
Normally, the functions rtapi_app_main and rtapi_app_exit
are automatically defined. With option rtapi_app no, they
are not, and must be provided in the C code.
When implementing your own rtapi_app_main, call the function
int export(char *prefix, long extra_arg) to register the
pins, parameters, and functions for prefix.
- option data type
- (default: none)
If specified, each instance of the component will have an associated
data block of type (which can be a simple type like float
or the name of a type created with typedef).
- option extra_setup yes
- (default: no)
If specified, call the function defined by EXTRA_SETUP for
each instance. If using the automatically defined rtapi_app_main,
extra_arg is the number of this instance.
- option extra_cleanup yes
- (default: no)
If specified, call the function defined by EXTRA_CLEANUP
from the automatically defined rtapi_app_exit, or if an
error is detected in the automatically defined rtapi_app_main.
If an option's VALUE is not specified, then it is equivalent to specifying
option ... yes. The result of assigning an inappropriate
value to an option is undefined. The result of using any other option
is undefined.
C++-style one-line comments (// ...) and C-style multi-line
comments (/* ... */) are both supported in the
declaration section.
Though HAL permits a pin, a parameter, and a function to have the
same name, comp does not.
Based on the items in the declaration section, comp creates
a C structure called struct state. However, instead of referring
to the members of this structure (e.g., *(inst->name)),
they will generally be referred to using the macros below. The details
of struct state and these macros may change from one version
of comp to the next.
- FUNCTION(name)
- Use this macro to begin the definition of a realtime
function which was previously declared with 'function NAME'.
The function includes a parameter 'period' which is the integer
number of nanoseconds between calls to the function.
- EXTRA_SETUP()
- Use this macro to begin the definition of the
function called to perform extra setup of this instance. Return a
negative Unix errno value to indicate failure (e.g., return
-EBUSY on failure to reserve an I/O port), or 0 to indicate success.
- EXTRA_CLEANUP()
- Use this macro to begin the definition of the
function called to perform extra cleanup of the component. Note that
this function must clean up all instances of the component, not just
one. The 'pin_name', 'parameter_name', and 'data' macros may not
be used here.
- pin_name
-
- parameter_name
- For each pin pin_name or param parameter_name
there is a macro which allows the name to be used on its own to refer
to the pin or parameter.
- data
- If 'option data' is specified, this macro allows access
to the instance data.
- fperiod
- The floating-point number of seconds between calls to
this function.
Place the .comp file in the source directory emc2/src/hal/components
and re-run make. Comp files are automatically detected
by the build system.
If a .comp file is a driver for hardware, it may be placed
in emc2/src/hal/components and will be built except if emc2
is configured as a userspace simulator.
comp can process, compile, and install a component in a single
step, placing example.ko in the emc2 realtime module directory:
-
- comp -install example.comp
Or, it can process and compile in one step, leaving example.ko
in the current directory:
-
- comp -compile example.comp
Or it can simply process, leaving example.c in the current
directory:
-
- comp example.comp
comp can also compile and install a component written in
C, using the -install and -compile options
shown above:
-
- comp -install example2.c
man-format documentation can also be created from the information
in the declaration section:
-
- comp -document example.comp
The resulting manpage, example.9 can be viewed with
-
- man ./example.9
or copied to a standard location for manual pages.
This component functions like the one in 'blocks', including the default
value of 1.0. The declaration ``function _'' creates functions
named 'constant.0', etc.
-
- component constant;
pin out float out;
param r float value = 1.0;
function _;
option extra_setup yes;
;;
FUNCTION(_) { out = value; }
This component computes the sine and cosine of an input angle in radians.
It has different capabilities than the 'sine' and 'cosine' outputs
of siggen, because the input is an angle, rather than running freely
based on a 'frequency' parameter.
The pins are declared with the names sin_ and cos_
in the source code so that they do not interfere with the functions
sin() and cos(). The HAL pins are still called sincos.<num>.sin.
-
- component sincos;
pin out float sin_ out;
pin out float cos_ out;
pin in float theta in;
function _;
;;
#include <rtapi_math.h>
FUNCTION(_) { sin_ = sin(theta); cos_ = cos(theta); }
This component is a driver for a fictional card called ``out8'',
which has 8 pins of digital output which are treated as a single 8-bit
value. There can be a varying number of such cards in the system,
and they can be at various addresses. The pin is called out_
because out is an identifier used in <asm/io.h>.
It illustrates the use of EXTRA_SETUP and EXTRA_CLEANUP
to request an I/O region and then free it in case of error or when
the module is unloaded.
-
-
component out8;
pin out u32 out_ "Output value; only low 8 bits are used";
param r u32 ioaddr;
function _;
option count_function;
option extra_setup;
option extra_cleanup;
option constructable no;
;;
#include <asm/io.h>
#define MAX 8
int io[MAX] = {0,};
RTAPI_MP_ARRAY_INT(io, MAX, "I/O addresses of out8 boards");
int get_count(void) {
int i = 0;
for(i=0; i<MAX && io[i]; i++) { /* Nothing */ }
return i;
}
EXTRA_SETUP() {
if(!rtapi_request_region(io[extra_arg], 1, "out8")) {
// set this I/O port to 0 so that EXTRA_CLEANUP does not release the IO
// ports that were never requested.
io[extra_arg] = 0;
return -EBUSY;
}
ioaddr = io[extra_arg];
return 0;
}
EXTRA_CLEANUP() {
int i;
for(i=0; i < MAX && io[i]; i++) {
rtapi_release_region(io[i], 1);
}
}
FUNCTION(_) { outb(out_, ioaddr); }
-
- component hal_loop;
pin out float example;
This fragment of a component illustrates the use of the hal_
prefix in a component name. loop is the name of a standard
Linux kernel module, so a loop component might not successfully
load if the Linux loop module was also present on the system.
When loaded, halcmd show comp will show a component called
hal_loop. However, the pin shown by halcmd show
pin will be loop.0.example, not hal-loop.0.example.
-
2006-11-29