RE: Hardware Abstraction Layer - Rev 0.01



John,

My vote is to keep this topic here for now, till we hear from Fred or
Will that they would like to formally move this list to sourceforge.  A
quick look at the EMC forums at sourceforge suggest almost no activity
currently... Although hopefully we can change that.

With regard to your HAL proposal, I strongly favor the implementation of
a true hardware abstraction layer, for all the reasons you outlined.
Unfortunately, I don't the technical perspective to voice an opinion of
your implementation approach yet, but I'm in the process of trying to
"recruit" some programming resources to help support this project.  In
that regard, give some thought as to how the work might be partitioned
up, as this does seem like an extensive upgrade / rewrite of EMC.  Feel
free to contact me off list if you would like to discuss some options.

One addition request / comment / question.  In the case of an
implementation where the servo PID loop is closed with external
hardware, could positional data from the trajectory planner just be
"passed through" the servo loop routine and output via the HAL with
minimal changes to the architecture?  I guess what I'm really suggesting
is that we include a feature in this version whereby this is made
feasible and efficient.   Perhaps a var in the init file could be
defined that would "enable" position or velocity mode?  Given the servo
loop speed at which the current implementation is able to run using a
1GHz processor, I'm not convinced at all this is necessary now... But
looking to the future it would seem useful.  And I'm thinking it might
preclude the need for that higher level API we discussed.

Craig Edwards

-----Original Message-----
From: emc-at-nist.gov [emc-at-nist.gov] On Behalf Of John Kasunich
Sent: Monday, May 05, 2003 12:48 AM
To: Multiple recipients of list
Subject: Hardware Abstraction Layer - Rev 0.01



The following is a description of a possible hardware abstraction layer
for EMC that I put together since our discussions at NAMES. This
probably should be posted to the developers list at Sourceforge, but I
don't know if all the right people are subscribed there yet.

To those who care about this stuff:  When should we make the move to the
other list?  And what do you think about the HAL?

To those who don't care about this stuff:  Sorry for the long message.

John Kasunich

---------------------------------------
Background

The hardware abstraction layer (HAL) is an attempt to
make EMC work with a wider variety of I/O hardware, and
to make configuring a machine easier.

There are actually two levels of hardware that we need
to consider.  The lowest level is the computer's I/O
hardware.  This can be as simple as a parallel printer
port, or as complex as a Gecko G2002.  (The G2002 has
up to 6 axis cards.  Each card has either a step pulse generator or a
D/A converter and an encoder counter. Each axis card also has a couple
bits of generic digital I/O.  All the axis cards are driven by a
dedicated micro, which communicates with the main PC using USB.)

Even though the printer port and the G2002 are totally different in
capabilities, they can both be viewed as simple I/O.  Each provides some
mix of digital inputs, digital outputs, analog inputs, and analog
outputs. (I am using the term digital to refer to single bit, on/off
signals, and analog to refer to signals with a range of values.  The A
and B signals of an encoder are digital signals, but if they are
connected to a counter, the counter output is an "analog" signal.
Likewise, a PWM or step frequency signal is an "analog" signal.  Of
course, DAC outputs are analog signals as
well.)

Since both the parallel port and the G2002 consist of
a mix of analog and digital I/O, both can be modeled
as a set of variables in shared memory space.  Then
all that needs to be done is to allow any of those
variables to be linked to the appropriate places in
the rest of the EMC code.

The second level of hardware is more difficult to
address.  It includes things like spindle drives, tool changers, lube
systems, and so on.  These things are not electronic, they are
electrical and mechanical. In many cases, they will require non-trivial
control algorithms involving timing, sequencing, and perhaps even
operator interaction.  Currently that control is done in the I/O task.
The HAL does not address that control.

An example may be the best way to illustrate what
the HAL does and does not do.

Assume you have a toolchanger.  It has a motor that
can rotate the turret either direction, and a switch
that closes momentarily each time a turret slot passes
the loading station.  It also has a pneumatically
actuated loading arm and a drawbar release.  The code
to control that toolchanger needs to sequence the
drawbar and loading arm, then rotate the turret while
counting slots, until the right slot is aligned with
the loading station.  Then it has to activate the arm
and drawbar again to load the new tool.  The HAL does
_not_ do any of that logic.  Today, that control would
be done in a C program.  In the future, it might be
possible to do it in a ladder logic program or some
other way.

What the HAL _does_ do is control the routing of the
control signals from the C (or PLC) program through
the I/O to the toolchanger.  The C program manipulates variables like
"drawbar" and "turret_ccw".  The HAL allows you to connect "drawbar" to
pin 7 of a parallel port, and "turret_ccw" to pin 23 of a Servo-to-Go
card, or whatever other configuration is required.  That means that the
control program for that particular type of toolchanger does not have to
be re-written to support different I/O devices.


Terminology:

HAL runtime data:  Memory mapped data, representing both
EMC side variables such as "axis[0].hi_limit" and hardware
(HW) side variables such as "printerport.pin_12".  These variables are
updated in realtime as the program runs.

HAL init data:  Memory mapped data, representing init parameters that do
not change while the machine runs.  This includes info about which
drivers are loaded, what variables they export, and how EMC side and HW
side variables are interconnected.

HAL runtime code:  Core code that runs once per servo loop.
It implements linking of EMC side and HW side variables,
and calls each driver's runtime code.

Driver:  A driver supports one particular type of I/O
hardware, such as a parallel port, STG card, etc.  Drivers
are loadable modules, and are insmod'ed based on the ini
file.  Basic drivers do not create realtime threads of
their own, instead they are called by the HAL runtime code. However,
some drivers (like software step pulse generators) will create realtime
threads that run much faster than the servo loop.

Driver runtime code:  Code that is called by the HAL
runtime code once per servo cycle to update the hardware.
It takes data from HW side variables like "printerport.
pin12" packs the bits into bytes, and writes them to
the hardware.  Likewise it reads bytes from the hardware
and unpacks them to individual HW side variables.  A
driver that implements software step and direction would convert its
floating point input to an int at this point, so that the faster
realtime thread could work in fixed point only.  Ethernet or firewire
drivers would assemble and dissassemble packets at this point.

Driver init code:  Code that runs when the driver is
insmod'ed.  It adds the driver's specific list of HW
side variables to the HAL runtime data, and adds their
names and linkage information to the HAL init data.


Design

The proposed HAL architecture makes some assumptions
about the system:

1)  There is a single master servo rate.  (This is how
EMC works today - it is possible to request different
rates for each axis in the ini file, but the fastest
one is used.)  The proposed design does not require
all servo loops to run at the same rate, but it does
require that the HAL update all the I/O at the rate
of the fastest servo loop.  This should not be a
problem, but the I/O update code needs to be simple
and FAST to allow fast servo loops.

2)  There are only two types of I/O, analog and digital. Digital values
are either TRUE or FALSE.  A "bit" data type is typedef'ed for digital
data:

typedef enum { FALSE = 0, TRUE = 1 } bit;

(C++ defines a "bool" type, but I'm not sure it is
supported in C.  If it is, it would be better than
creating a custom type.)  "bit" values occupy at
least one byte - no packing is done at the HAL level.
This prevents problems when two threads try to modify
different bits in the same byte.

Analog values are represented as "doubles".  Most of the
code that manipulates these values will be running in a
thread that already uses floating point, so there will
be no performance hit for saving/restoring the FP state. Drivers that
use a faster thread (for example software
step/direction) could convert from float to int in the
slow thread, and run the fast thread entirely with
fixed point.  (But they don't have to, the decision is
made by the driver implementer.)


Data Structures:

HAL Runtime Data:

   EMC Side Data:

     /* structure containing all EMC side variables for an abstracted 
motion axis */
     typedef struct
     {
       double   PosFb;               /* input:   position feedback, in 
counts */
       double   VelCmd;              /* output:  velocity command, in 
counts/second */
       bit      Home;                /* input:   true means home switch 
tripped*/
       bit      LoLim;               /* input:   true means low limit 
switch tripped */
       bit      HiLim;               /* input:   true means high limit 
switch tripped */
       bit      Fault;               /* input:   true means amp has
faulted */
       bit      Clamp;               /* output:  true means axis should
be 
clamped */
       bit      Enable;              /* output:  true means amp should
be 
enabled */
     /* Anything else? */
     } HAL_AXIS;

     There would be one of these for each axis.  There would also be 
structures for
     the spindle(s), and variables or structures for other I/O such as
coolant,
     lube, toolchangers, etc.

   HW Side Data:

     /* structure containing all HW side variables for a printer port */
     /* defined in the printerport driver and present only when that
driver 
is loaded */
     typedef struct
     {
       bit     Pin1;                 /* output:  true drives pin 1 hi */
       bit     Pin14;                /* output:  true drives pin 14 hi
*/
       bit     Pin16;                /* output:  true drives pin 16 hi
*/
       bit     Pin17;                /* output:  true drives pin 17 hi
*/
       bit     Pin2;                 /* output:  true drives pin 2 hi */
       bit     Pin3;                 /* output:  true drives pin 3 hi */
       bit     Pin4;                 /* output:  true drives pin 4 hi */
       bit     Pin5;                 /* output:  true drives pin 5 hi */
       bit     Pin6;                 /* output:  true drives pin 6 hi */
       bit     Pin7;                 /* output:  true drives pin 7 hi */
       bit     Pin8;                 /* output:  true drives pin 8 hi */
       bit     Pin9;                 /* output:  true drives pin 9 hi */
       bit     Pin10;                /* input:   true when pin 10 is hi
*/
       bit     Pin11;                /* input:   true when pin 11 is hi
*/
       bit     Pin12;                /* input:   true when pin 12 is hi
*/
       bit     Pin13;                /* input:   true when pin 13 is hi
*/
       bit     Pin15;                /* input:   true when pin 15 is hi
*/
     } DRV_PPBITS;

     There would be a data structure for each driver loaded.  The
structure 
would
     vary depending on the I/O mix provided by the driver.

HAL Init Data:

   Link Data:

     /* structures used to establish a link between an EMC side variable

and a HW side variable */
     typedef struct
     {
       double *src;         /* pointer to data source */
       double *dst;         /* pointer to data destination */
     } HAL_LINK_DBL;

     typedef struct
     {
       bit   *src;          /* pointer to data source */
       bit   *dst;          /* pointer to data destination */
       bit    invert;       /* true means an inverting link */
     } HAL_LINK_bit;

     /* arrays of links, filled in from ini file */

     HAL_LINK_DBL      hal_output_links_dbl[MAX_LINKS_DBL];
     HAL_LINK_BIT      hal_output_links_bit[MAX_LINKS_BIT];
     HAL_LINK_DBL      hal_input_links_dbl[MAX_LINKS_DBL];
     HAL_LINK_BIT      hal_input_links_bit[MAX_LINKS_BIT];

   Driver Function pointers:

     An array of pointers to each driver's runtime code


Sqeuence of operation:

1)  Hardware interrupt occurs (either system timer, or hardware device)
2)  Run through both output link arrays, copying data from EMC side to
HW side.
3)  Run through driver function pointer array, calling driver runtime
code.
4)  Driver runtime code copies output data from HW side structs to the 
actual hardware.
5)  Driver runtime code copies input data from the actual hardware to
the 
HW side structs.
6)  Driver runtime code returns.
7)  Run through both input link arrays, copying data from HW side to EMC
side.
8)  End of HAL code, begin executing servo loop code (emcmot).

MAJOR ISSUES:

The runtime stuff is pretty simple, in fact I coded it in less than an
hour. Init is the hard part.  The drivers must export the names and
locations of their runtime data in a way that allows the ini file to
specify links. Links might look like this in the ini file:

[INLINKS]
axis[0].pos_fb   =  stg[1].chan[2].encoder
axis[0].hi_limit =  parallelport[0].Pin11
axis[0].lo_limit =  parallelport[0].Pin11
axis[0].home     = !stg[1].digio[20]

[OUTLINKS]
stg[1].chan[2].dac   =  axis[0].vel_cmd
parallelport[0].Pin3 =  axis[0].enable

Note in the INLINKS section that both the high limit and
low limit are linked to the same input, and the home input
is low true, which generates an inverting link.

There is also no reason why a link has to go from the HW
side to the EMC side, or vice-versa.  A link from one EMC variable to
another would be perfectly legal, as would a link from one HW side
variable to another.

In fact, a PLC could be loaded just like a driver.  It would export a
number of bits that would be coils and contacts in it's ladder logic.
Links would connect the coils and contacts to EMC side and/or HW side
variables as needed.

I'm learning more about the ini file and the functions that manipulate
it, but I'm not ready to propose a detailed mechanism to get from the
list of links above to a completely filled in set of link arrays.  I
just wanted to post this to get other people looking at it.  Please
review and let me know what you think.

John Kasunich






Date Index | Thread Index | Back to archive index | Back to Mailing List Page

Problems or questions? Contact