Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions debian/linuxcnc-uspace-dev.install
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
usr/bin/halcompile
usr/bin/halcompupdate
usr/include/linuxcnc
usr/lib/liblinuxcnc.a
usr/lib/*.so
Expand Down
1 change: 1 addition & 0 deletions debian/linuxcnc-uspace-dev.manpages
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
usr/share/man/man1/halcompile.1
usr/share/man/man1/halcompupdate.1
usr/share/man/man3/*
71 changes: 69 additions & 2 deletions docs/src/hal/comp.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,11 @@ In this case, r-strings are particularly useful, because the backslashes in an r
r"\fIexample\fB"
----

* 'TYPE' - One of the HAL types: 'bit', 's32', 'u32', 's64', 'u64' or 'float'.
The names 'signed' and 'unsigned' may also be used for 's32' and 'u32' but 's32' and 'u32' are preferred.
* 'TYPE' - One of the HAL types: 'real', 'bool', 'sint' or 'uint'.
'real' is a floating point value, 'bool' a boolean, and 'sint' and 'uint' are 64-bit signed and unsigned integers.
'si32' and 'ui32' are 'sint' and 'uint' pins that the component reads and writes as 32-bit values; they may be retired once components are 64-bit clean.
The legacy types 'float', 'bit', 's32', 'u32', 's64', 'u64', 'signed' and 'unsigned' are still accepted, with a warning.
See <<sub:hal-comp-migration,Migrating to the new HAL API>>.
* '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.
Expand Down Expand Up @@ -481,6 +484,15 @@ The details of `struct __comp_state` and these macros may change from one versio
+
When the item is a conditional item, it is only legal to refer to it when its 'condition' evaluated to a nonzero value.

* `pin_name_set(`__value__`)` or `param_name_set(`__value__`)` - For each 'out' or 'io' pin and each parameter
of a new-style type ('real', 'bool', 'sint', 'uint', 'si32', 'ui32'), there is a macro which sets the value of the pin or parameter.
For arrays the form is 'pin_name_set(idx, value)'.
With the new-style types, assigning to an 'out' or 'io' pin or to a parameter with plain C assignment is not possible;
the '_set' macro must be used instead (reading stays transparent through the bare name).
The '_set' macro evaluates to the value that was set, so chained use like 'a_set(b_set(x))' works.
There is also a 'pin_name_ptr' macro, but with new-style types it evaluates to an opaque reference
which can only be used with the 'hal_get_*' and 'hal_set_*' functions; it cannot be dereferenced.

* 'variable_name' - For each variable 'variable_name' there is a macro which allows the name to be used on its own to refer to the variable.
When 'variable_name' is an array, the normal C-style subscript is used: 'variable_name[idx]'.
* 'data' - If "option data" is specified, this macro allows access to the instance data.
Expand All @@ -489,6 +501,61 @@ When the item is a conditional item, it is only legal to refer to it when its 'c
This macro iterates over all the defined instances.
Inside the body of the loop, the 'pin_name', 'parameter_name', and 'data' macros work as they do in realtime functions.

[[sub:hal-comp-migration]]
== Migrating to the new HAL API

HAL accesses pin and parameter values through strongly typed getters and setters, not direct memory access.
New-style declaration types replace the legacy types:

[cols="1,1,3",options="header"]
|===
| Legacy type | New type | Underlying C type
| 'float' | 'real' | 'rtapi_real' (floating point)
| 'bit' | 'bool' | 'rtapi_bool' (boolean)
| 's32' | 'si32' | 'rtapi_s32' (32-bit signed)
| 'u32' | 'ui32' | 'rtapi_u32' (32-bit unsigned)
| 's64' | 'sint' | 'rtapi_sint' (64-bit signed)
| 'u64' | 'uint' | 'rtapi_uint' (64-bit unsigned)
| 'signed' | 'si32' | 'rtapi_s32' (32-bit signed)
| 'unsigned' | 'ui32' | 'rtapi_u32' (32-bit unsigned)
|===

'halcompile' still accepts the legacy declaration types with a warning, but the component code must use the new access:

* reading a pin or parameter is unchanged (the bare name, or 'name(idx)' for arrays, evaluates to the value);
* writing an 'out' or 'io' pin or a parameter requires the generated 'name_set(value)' macro
(or 'name_set(idx, value)' for arrays) instead of plain assignment;
* 'name_ptr' is an opaque reference for use with 'hal_get_*'/'hal_set_*' and can no longer be dereferenced.

Existing components can be migrated automatically with the 'halcompupdate' tool:

----
halcompupdate mycomponent.comp # show a diff of the required changes
halcompupdate -i mycomponent.comp # rewrite the file in place (keeps a .bak)
----

The tool converts the declaration types and rewrites writes to pins and parameters
(including compound assignments and dereferences of 'name_ptr') to the '_set' form.
A 32-bit pin or parameter becomes 'si32' or 'ui32', which keeps its current behaviour, and the tool points out each one.

[IMPORTANT]
'si32' and 'ui32' are a stopgap, not the end of the migration.
Every converted component must be made 64-bit clean, moving its 'si32' and 'ui32' pins and parameters to 'sint' and 'uint'.
This requires an analysis of the code; no tool can do it.
A value that passes through 32-bit intermediate storage or a 32-bit calculation is silently truncated:
check every local variable, intermediate result, cast and helper function parameter a pin value passes through,
and every place that relies on the 32-bit range or on wrap-around.

It deliberately does not rename pins or parameters, because their names are part of the HAL configuration interface.
It does point out names that still spell a legacy type, though: a pin, parameter, component or
function name that contains 'float', 'bit', 's32', 'u32', 's64' or 'u64' as a name segment (for
example 'out_s32', 'input_bit' or 'conv_s32_float') is noted together with the new-style spelling
(for example 'out_sint' or 'conv_sint_real'); the rename is optional and left to the author, who
decides whether the word really meant the type (a pin named 'sel-bit' counts bits of a value).
Components that use the legacy 'hal_pin_*_new'/'hal_param_*_new' creation functions or that pass
the address of a pin ('&name') to helper functions cannot be converted automatically;
'halcompupdate' prints a warning for those cases so they can be converted by hand.

== Components with one function

If a component has only one function and the string `"FUNCTION"` does not appear anywhere after `;;`,
Expand Down
3 changes: 3 additions & 0 deletions docs/src/man/man1/halcompile.1.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ documentation, https://linuxcnc.org/docs/html/hal/comp.html#sec:halname

== SEE ALSO

* *halcompupdate*(1) to migrate existing *.comp* files to the new HAL
pin/param API (new-style declaration types and *<name>_set()* accessors)

* _Halcompile_ / _HAL Component Generator_ in the LinuxCNC documentation for a
full description of the *.comp* syntax, along with examples

Expand Down
129 changes: 129 additions & 0 deletions docs/src/man/man1/halcompupdate.1.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
= halcompupdate(1)

== NAME

halcompupdate - Migrate HAL .comp components to the new HAL pin/param API

== SYNOPSIS

*halcompupdate* [--in-place] [--no-backup] [--check] [--no-c-types] [--quiet] compfile...

== DESCRIPTION

*halcompupdate* converts HAL components written for the legacy HAL API
(pins and params declared with the types *float*, *bit*, *s32*, *u32*,
*s64*, *u64*, *signed* or *unsigned* and written with plain C assignment)
to the new HAL API, where pins and params are declared with the types
*real*, *bool*, *si32*, *ui32*, *sint* and *uint* and written with the
generated *<name>_set(value)* accessor.

halcompile(1) still accepts the legacy declaration types with a warning,
but plain C assignment to a pin or parameter no longer compiles.

The following transformations are applied:

* Declaration types are replaced: *float* -> *real*, *bit* -> *bool*,
*s32* -> *si32*, *u32* -> *ui32*, *s64* -> *sint*, *u64* -> *uint*,
*signed* -> *si32*, *unsigned* -> *ui32*. *si32* and *ui32* keep the
current 32-bit behaviour, and each one is noted with its line number.
They are a stopgap: the component must then be made 64-bit clean,
moving them to *sint* and *uint*. *That requires an analysis of the
code*: a value that passes through 32-bit intermediate storage or a
32-bit calculation is silently truncated, so every local variable,
intermediate result, cast and helper function parameter a pin value
passes through must be checked, as must every place that relies on the
32-bit range or on wrap-around. The type *port* is not converted yet.
* Assignments to *out* and *io* pins and to parameters are rewritten to
the *<name>_set(...)* form, including compound assignments
(*name += x* becomes *name_set(name + (x))*), increments/decrements
and array pins (*name(i) = x* becomes *name_set(i, x)*).
* Dereferences of the old pin pointer macro are rewritten:
***name_ptr*** reads become *(name)* and ***name_ptr = x*** writes
become *name_set(x)*.
* Legacy C types are modernized (*double* -> *rtapi_real* and so on).
The legacy *hal_*_t* types carry a *volatile* qualifier, which is
preserved in body code (*hal_bit_t x* becomes *volatile rtapi_bool x*),
so the conversion is semantics-identical whether or not the code relies
on the volatility. Each such conversion is reported with its line
number so the spots can be reviewed and the qualifier dropped by hand
where it is not needed. In *variable* declarations the qualifier cannot be
expressed, so *hal_*_t* types there are reported and left unchanged for
manual conversion. Pointers to the legacy types referenced HAL memory
and become opaque references in the new API; those are warned about and
left unchanged. Use *--no-c-types* to skip the C type conversion.

Reading pins and params is unchanged: the bare name (or *name(idx)* for
arrays) evaluates to the value in both APIs.

Pin and parameter names are never changed, because they are part of the
HAL configuration interface used by .hal files. A name that spells out
a legacy type, however, is pointed out: *halcompupdate* notes every
name - of a pin, a parameter, the component or a function - that
contains a segment naming a type that will be removed at the API break
(*float*, *bit*, *s32*, *u32*, *s64*, *u64*) and suggests the
new-style spelling, for example *out_s32* -> *out_sint* or
*conv_s32_float* -> *conv_sint_real*. The component name is the
loadrt/loadusr argument and the module name, and functions are exported
as
*comp.N.function*, so a renamed component or function name affects
existing configurations as well. The note is optional, does not modify
the file, and does not affect the *--check* exit status; the author
decides whether the word in the name really meant the type (a pin named
*sel-bit* counts bits of a value, a pin named *float_switch* is a
hardware float switch).

Components that use the legacy *hal_pin_*_new*/*hal_param_*_new* creation
functions directly, or that pass the address of a pin (*&name*) to helper
functions, cannot be converted automatically. *halcompupdate* prints a
warning for those cases so they can be converted by hand.

After each file a summary line reports how many mechanical changes were
applied, how many constructs were left for manual review, how many pins
and parameters were kept 32-bit, and how many names
mention a legacy type (optional rename); if nothing needed to be
converted, it reports the name notes only. Always review the diff and
test the converted component before use.

== OPTIONS

*compfile...*::
One or more .comp files to convert.
Without other options, a unified diff of the required changes is printed.

*-i*, *--in-place*::
Rewrite the files in place. A backup with the suffix *.bak* is kept
unless *--no-backup* is given.

*--no-backup*::
With *--in-place*, do not keep a *.bak* backup.

*--check*::
Do not write anything; exit with status 1 if any file would be changed.
Useful in build systems to verify that components are migrated.

*--no-c-types*::
Only convert pin/param declarations and accesses; do not modernize
C types in the component body.

*-q*, *--quiet*::
Suppress warnings and notes on stderr.

== EXAMPLES

Show what would change:

----
halcompupdate mycomponent.comp
----

Migrate in place:

----
halcompupdate -i mycomponent.comp
----

== SEE ALSO

*halcompile*(1), the _Halcompile_ / _HAL Component Generator_ section in
the LinuxCNC documentation for a full description of the *.comp* syntax
and the new HAL API.
8 changes: 7 additions & 1 deletion src/hal/utils/Submakefile
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,19 @@ endif
$(ECHO) Copying python script $(notdir $@)
$(Q)(echo '#!$(PYTHON)'; sed '1 { /^#!/d; }' $<) > $@.tmp && chmod +x $@.tmp && mv -f $@.tmp $@

../bin/halcompupdate: ../bin/%: hal/utils/%.py
@$(ECHO) Syntax checking python script $(notdir $@)
$(Q)$(PYTHON) -m py_compile $<
$(ECHO) Copying python script $(notdir $@)
$(Q)(echo '#!$(PYTHON)'; sed '1 { /^#!/d; }' $<) > $@.tmp && chmod +x $@.tmp && mv -f $@.tmp $@

../bin/mesambccc: ../bin/%: hal/drivers/mesa-hostmot2/%.py
@$(ECHO) Syntax checking python script $(notdir $@)
$(Q)$(PYTHON) -m py_compile $<
$(ECHO) Copying python script $(notdir $@)
$(Q)(echo '#!$(PYTHON)'; sed '1 { /^#!/d; }' $<) > $@.tmp && chmod +x $@.tmp && mv -f $@.tmp $@

TARGETS += ../bin/halcompile ../bin/elbpcom ../bin/mesambccc
TARGETS += ../bin/halcompile ../bin/elbpcom ../bin/mesambccc ../bin/halcompupdate
objects/%.py: %.g
@mkdir -p $(dir $@)
$(Q)$(YAPPS) $< $@
4 changes: 2 additions & 2 deletions src/hal/utils/halcompile.g
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,9 @@ def type2type(type_):
repl = typemap[type_]
if repl.endswith("32"):
nt = repl[:-2] + "nt"
Warn(f"Old type '{type_}' was replaced by '{repl}', but you should be upgrading to '{nt}'.")
Warn(f"Old type '{type_}' was replaced by '{repl}', but you should be upgrading to '{nt}'. halcompupdate(1) converts a .comp.")
else:
Warn(f"Old type '{type_}' has been replaced by '{repl}'")
Warn(f"Old type '{type_}' has been replaced by '{repl}'. halcompupdate(1) converts a .comp.")
return typemap[type_]
return type_

Expand Down
Loading
Loading