The unsigned integer question #4526
Replies: 6 comments 17 replies
|
With respect to Modbus, any system that uses 64-bit (flag) registers cannot be read using floating point or important information will be lost. It may currently be rare to find these systems, but it is not unheard of in implementations that use read/write-in-depth instead of classic modbus register addressing. Do we distinguish between the sets ℕ and ℤ? Normally, ℕ is a subset of ℤ. But in computers that is not the case with distinct ranges. If we are to abandon unsigned (ℕ), then we should also remove all use of unsigned types in the rest of the code. Arguing that the distinction is immaterial in one case but not the other would be inconsistent IMO. |
|
hal and code are 2 different things. It is called hardware abstraction layer, not hardware access layer. I dont understand where the idea comes from that having hardware registers exposed as hal pins is useful for anything. It is the drivers job to abstract the hardware. in the case with encoders, it scales it to machine units, so it becomes useful. for a vfd, it converts rpm to whatever the vfd needs. linuxcnc does not even have a setting for scaling anything, it is always scaled in the driver. so far, lut5 is the only answer that came up. I am talking about HAL, not about the code. Of course integer types are needed in the code. |
|
Conceding the two examples first: ilowpass and eoffset_per_angle are indeed scale-hack artifacts and would be better as float. I am not defending integer pins where a physical quantity is involved. If the list of integer pins were only those two, I would agree with you. But the original question, "is there any integer pin that can't be a float", still has concrete cases on the table, so let me ask about those directly.
My actual position is narrower than "keep everything": physical quantities should be float, counts and register images should stay integer, and bit patterns should stay unsigned. That gives us float + signed + unsigned, which is what we have. The two-type proposal (float + s64) answers cases 1 through 3 with either lost information or silent truncation, so I am asking: what does it buy that justifies that? |
|
I know that we are trying to get away from parameters, but some of the examples above are parameter-like rather than pin-like. To implement LUT5 with a float input pin, you would simply cast to int before indexing into the int. If that isn't what the user intended, then that's on them for specifying a stupid function value. |
|
Fair points on both. lut5.0.function is a param, and raw.write-data is a debug escape hatch; neither carries the pin argument on its own, so I will drop them. The case that remains is hm2_modbus. Its pins are not params and not debug hooks: they are the component's primary interface, created one per compiled mbcc item precisely so users can net register contents into their own logic (hm2_modbus.c:2983-3025, u64 at line 3006). The component is a protocol bridge; the device semantics live in the user's mbcc file, so there is nothing in the driver that could scale or decompose the register on the way out. For a u64 holding register, float loses bits above 2^53 and s64 reads [2^63, 2^64) as negative. That is the entire remaining question for the two-type proposal: what should that pin be? |
|
Let me concede the sharp version first: nothing on the table is bit-lossy on s64. A u64 register in [2^63, 2^64) keeps every bit on a signed pin; only the display and comparisons change. What unsigned carries is semantics and a guard, not information. So the question is whether that guard is worth a type. Precedent says yes. Classic Siemens S7 had no unsigned integers for decades and TIA added USINT..ULINT for the S7-1200/1500; Allen-Bradley Logix went the same way. Not for arithmetic: CIP, PROFINET, OPC UA and CoE object dictionaries are unsigned by definition, and a signed-only controller pushes the "if x < 0 then x + 65536" idiom onto every user at that boundary. LinuxCNC has three such boundaries in tree already (hm2_modbus, sserial streams, lcec generic). Dropping uint now walks the same path those vendors reversed. The busiest of those boundaries is CiA 402. lcec exposes 0x6041 statusword and 0x6040 controlword as u32 pins, and the drive state machine lives in a separate component, cia402.comp, which declares The API break has quietly built the untyped-word model: every integer pin is one 64-bit word, and si32/ui32 are accessor views on it. Only hal_link has not caught up; it still demands exact type equality, so a ui32 pin cannot be netted to a uint pin holding identical bytes. That is the mismatch users actually hit and the reason 29 conv components exist. Proposal, the middle ground:
Modbus then needs a policy, not a type. hm2_modbus already implements it: scaled items go out as real through an s64 cast (hm2_modbus.c:1991), raw register images go out as uint, and the mbcc chooses per item. Stated as policy: physical quantities are real, register images are uint. Two gaps to close so the raw path is usable: bitslice and bitwise take ui32 today, so a 64-bit flag register has no in-tree consumer, and the display tools should offer hex for uint pins (halcmd, halshow, halmeter at least), which removes the "reads negative" complaint at the root. What this costs relative to two numeric types: one extra case in every generic switch (mux_generic, sampler, streamer, halmodule, halcmd and the GUI widget libraries). That is the whole remaining price, and I think the guard plus the three protocol bridges are worth it. |
Uh oh!
There was an error while loading. Please reload this page.
@rene-dev about the signed and unsigned integer use, these are what I found.
encoder.N.counts => joint.N.jog-counts(s32 to s32): every MPG pendant configuration. In-tree alone: woodpecker mpg.hal, touchy, shuttle, xhc-hb04, and pico pendant.hal (rawcounts via ilowpass, s32 throughout). Motion's jog protocol is an exact per-cycle count delta (control.c:1066).iocontrol.0.tool-prep-number: axis_manualtoolchange.hal is pulled in by 50 in-tree inis, and smithy/924.hal nets it into classicladder s32 word logic on shipped commercial machines.Unsigned: hm2_modbus (in 2.9) maps raw device registers to pins, including 64-bit unsigned compounds (
hal_pin_u64_newf, plus a u64.offsettare doing modulo-2^64 subtraction). A totalizer register above 2^53 loses bits on a float pin; the range [2^63, 2^64) reads negative on s64. The pin is the user-facing register image, so "keep it driver-internal" does not apply.Bit patterns: hm2
raw.write-dataand bitwise (u32 pins), plus lut5 (stepconf generatessetp lut5.0.function 0x10000, a u32 param). On an integer pin, NaN and 2.5 are unrepresentable; todaysetp <int-pin> nanis rejected whilesetp <float-pin> nansucceeds, and(int32_t)NaNis 0x80000000 straight into an FPGA register. No driver checks isnan; the type is the guard.All reactions