Skip to content
Merged
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/*
1 change: 1 addition & 0 deletions docs/po4a.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@
[type: AsciiDoc_def] src/man/man1/halcmd.1.adoc $lang:build/adoc/$lang/man/man1/halcmd.1.adoc
[type: AsciiDoc_def] src/man/man1/halcmd_twopass.1.adoc $lang:build/adoc/$lang/man/man1/halcmd_twopass.1.adoc
[type: AsciiDoc_def] src/man/man1/halcompile.1.adoc $lang:build/adoc/$lang/man/man1/halcompile.1.adoc
[type: AsciiDoc_def] src/man/man1/halcompupdate.1.adoc $lang:build/adoc/$lang/man/man1/halcompupdate.1.adoc
[type: AsciiDoc_def] src/man/man1/halfileupdate.1.adoc $lang:build/adoc/$lang/man/man1/halfileupdate.1.adoc
[type: AsciiDoc_def] src/man/man1/halmeter.1.adoc $lang:build/adoc/$lang/man/man1/halmeter.1.adoc
[type: AsciiDoc_def] src/man/man1/halreport.1.adoc $lang:build/adoc/$lang/man/man1/halreport.1.adoc
Expand Down
90 changes: 90 additions & 0 deletions docs/src/hal/comp.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,10 @@ The documentation string is in "asciidoc" format.
For more information on this markup format, see 'ascidoctor(1)' and https://asciidoctor.org.

* 'TYPE' - One of the HAL types: `bool`, `sint`, `uint` or `real`.
`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 @@ -480,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 @@ -488,6 +501,83 @@ 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.

[[sub:hal-comp-legacy-types]]
=== The legacy 'hal_*_t' types

The types 'hal_bit_t', 'hal_float_t', 'hal_s32_t', 'hal_u32_t', 'hal_s64_t' and 'hal_u64_t' no longer exist.
'halcompupdate' does not convert them: it reports every use with its line number, and the component does not compile until each one is dealt with.

The legacy types were volatile values, the memory cell a pin pointer pointed at.
The types that replace them in the HAL API ('hal_bool_t', 'hal_real_t', 'hal_sint_t', 'hal_uint_t') are opaque references to a pin or parameter.
That makes the cases different:

* A variable of one of the legacy types is nearly always a mistake.
It holds a value only the component itself ever references and pays for a volatile access on every read and write.
Local and instance variables should use the plain type ('rtapi_bool', 'rtapi_real', 'rtapi_sint', 'rtapi_uint') after checking that nothing relies on the volatile.
* A pointer to one of the legacy types pointing into HAL memory cannot be carried over.
What probably was meant was to use plain types ('rtapi_bool', 'rtapi_real', 'rtapi_sint', 'rtapi_uint'), but this needs to be evaluated on a case-by-case basis.
The new HAL types are strictly references for pins and params and require 'hal_get_*' and 'hal_set_*' for access.

* Any other use of the legacy types needs to be evaluated and changed accordingly.

A component that compiles after 'halcompupdate' has not been checked by it.
The tool only makes the mechanical part cheap: read the diff and every warning, then test the component.

== 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 @@ -116,6 +116,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
122 changes: 122 additions & 0 deletions docs/src/man/man1/halcompupdate.1.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
= halcompupdate(1)

== NAME

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

== SYNOPSIS

*halcompupdate* [--in-place] [--no-backup] [--check] [--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)*.

The C types in the component are left alone. The legacy *hal_*_t* types no
longer exist. They were volatile values, nearly always wrong as variables,
and only the author can tell, so each use is reported with its line number
and left for manual conversion. A pointer to one of these types needs the same
evaluation: a plain type or, for a pin or param, a reference with *hal_get_** and
*hal_set_**. A conversion that compiles is
not a checked one: review the diff and test the component.

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 legacy type
(*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.

*-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 @@ -117,13 +117,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/halfileupdate ../bin/elbpcom ../bin/mesambccc
TARGETS += ../bin/halcompile ../bin/halfileupdate ../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