-
-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add C serialization support #115
Conversation
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks SO much for taking this on. This is a huge help. We need to clone, rename, and fixup the canard serialization code before I can fully review and approve.
As far as the canard serialization code, do you think that should be in this PR? Or is that a seperate issue that should be merged in before this gets fully reviewed? |
It'll have to be in the same PR to go into mainline (non-breaking changes and all). You can work on a feature branch if you want to iterate on smaller PRs. |
I like leaving it in this PR. When it comes closer to full-review time I'll be rebasing and isolating things into very rigid commits. |
Going through your comments in more detail @pavel-kirienko
|
I've literally just copied over all of the libcanard serialization functions, changing only naming and their storage to I'm not sure if there are any tweaks to make here. Also I'm not over the moon with this method of doing things, because we have to manually copy over any changes. (I don't have any better solutions though ¯_(ツ)_/¯) |
I suggest we define an alias instead of using raw (
I don't think copying over changes will be necessary because the point here is to be compliant with the Specification rather than another implementation, so the implementations are independent from each other. But I agree that it just seems odd having to copy-paste a bunch of code from one place to another. Yeah. Maybe in the future, we should just drop Regarding naming: we care about naming a lot here. Basically, if the name cannot be found on the map of Canada, it won't do. So instead of Since Nunavut specializes in DSDL code generation, we can simplify the names like The current implementation of
Assertion checks in the source definition have zero effect on the output. Assertion checks exist to let the data type author verify that the field layout matches the expectations (actually, they can be used for more than that but it doesn't matter atm). Nested types can be handled by checking the alignment before the nested type field; if the type is aligned, then its (de)serialization can be delegated, otherwise, the (de)serialization logic can be emitted in-place as if the function was inlined. See OpenCyphal/pydsdl#24. For example, imagine we have some type
In this example, the serialization function of Eventually it would be great to support full zero-cost serialization as described in https://forum.uavcan.org/t/future-zero-cost-serialization-constraint/469. It doesn't require any changes from the language specification because the implementation can just deduce if the layout is compatible with the native type layout automatically at code generation time. We'll get to that eventually.
Yes, you got it right. The rules follow from MISRA. |
@dagar FYI |
@davidlenfesty anything on deserialization at this point? |
@TSC21 Nothing yet, but most of the architectural things I'm solving with the serialization should pretty much map 1:1 to deserialization so it'll be quick when I get to it. |
Perfect! Thanks! |
@davidlenfesty @pavel-kirienko what are we missing here at this stage? |
@TSC21 time :) More specifically I need to:
|
@davidlenfesty thank you! Really great job so far! |
ce50c28
to
914d522
Compare
Nice little update: I've confirmed serialization works against pyuavcan (using TODO before I open for full review:
@pavel-kirienko @thirtytwobits I'm sure I can figure out, but if I could get the code path for CLI argument -> passing a Jinja2 variable from someone more familiar that would probably save me a significant amount of time. |
@davidlenfesty I would like to add a few early comments:
|
Also noticed a bug in existing code: type_MAX_SERIALIZED_REPRESENTATION_SIZE_BYTES is defined as bit length. Commenting so I don't forget. |
fc23634
to
6d6217b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deserialization is finished! 🎉 Well, it compiles anyways.
A few more notes:
- I don't want to support full zero-cost serialization yet (I am very low on time and validating that on every C architecture could get nasty fast)
- I have yet to optimize arrays of bools.
- I have not tested this against pyuavcan at all. If there is any test harnessing anyone has set up between the different UAVCAN libraries I would like to know so I could test.
- the support files (
nunavut*.h
) do not get included, some bug needs to get fixed there methinks.
I personally would like to skip bool array optimization and zero-cost serialization in this PR, as I would like to get a minimum viable product into master (I'm not being selfless making this PR, I need this for some of my projects ;)), and then work on another PR with those features. But if optimizing bools is important I can get it in in this PR too.
I ran the libcanard tests on my implementation and the only thing that was failing was:
This is because we do discard the diagnostic information (from section 6.2 of IEEE754):
Notice the "should". As such your test is not guaranteed to succeed for compliant implementations. I'm willing to open an issue to try to restore this information but it seems like something that will take more research especially as we are losing bits for a value whose structure we don't understand (i.e. do we discard high bits or low bits? Is there some assumption about the diagnostic data we can make that should be accounted for? How does the receiver tell the difference between a signaling NaN with 0x00 as it's diagnostic when we have to enforce a non-zero bit to distinguish from INFINITY? etc) This does pass:
|
My point above was that your new implementation is less incorrect than the
original, but there are some remaining issues.
The test from the libcanard master are invalid, can you please try the new
ones from the v1 pull request? I updated them yesterday.
…On Mon, Oct 5, 2020, 08:13 Scott Dixon ***@***.***> wrote:
I ran the libcanard tests on my implementation and the only thing that was
failing was:
ASSERT_FLOAT_EQ(0b0111111111111111, nunavutFloat16Pack(std::nanf(""))); // nan
This is because we do discard the diagnostic information (from section 6.2
of IEEE754):
To facilitate propagation of diagnostic information contained in NaNs, as
much of that information as possible should be preserved in NaN results of
operations.
Notice the "should.". As such your test is not guaranteed to succeed for
compliant implementations. I'm willing to open an issue to try to restore
this information but it seems like something that will take more research
especially as we are losing bits for a value whose structure we don't
understand (i.e. do we discard high bits or low bits? Is there some
assumption about the diagnostics we can make that should be accounted for?
How does the receiver tell the difference between a signaling NaN with 0x00
as it's diagnostic when we have to enforce a non-zero bit to distinguish
from INFINITY? etc)
This does pass:
EXPECT_TRUE(std::isnan(nunavutFloat16Unpack(nunavutFloat16Pack(std::nanf(""))))); // nan
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#115 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAZFIZCJSX2UG7EQR6UE6KTSJFIXFANCNFSM4OQWADDA>
.
|
Yeah, this is what I did. See thirtytwobits@0932091 Again, you can't assert that the bit pattern in the significand will be a known value after calling pack. |
@thirtytwobits Yes I should be able to add in the buffer size params sometime in the next couple days. |
@thirtytwobits With this change in 2203ae9#diff-5b6bfcb09795f7e8909ef7aeaf1a0bbcR460, In x86 mode ( As I said earlier, I never actually eneavored to read the IEEE 754 standard, relying instead on the existing conversion functions contributed by someone else. I just noticed that on Wikipedia it says this:
The standard apparently makes it impossible for us to distinguish between signaling and quiet NaNs in a platform-agnostic way. The existing logic happens to be working well on AMD64 but per the standard that is a mere coincidence; it breaks in the x86 mode but nobody says it wouldn't. So what I suggest is that we replace this: if ((0x400000UL & in.bits) != 0) // NOLINT NOSONAR
{
out = 0x7E00U; // NOLINT NOSONAR
}
else
{
out = 0x7D00U; // NOLINT NOSONAR
} with this: out = 0x7E00U; // NOLINT NOSONAR I would like to remind here about this issue, as it seems serious:
|
Huh. Perhaps this was added in the latest revision of 754? (from IEEE Std 754-2019)
That said, the specification does say "should" and not "shall". I missed that nuance when I wrote my tests. |
Okay. Do we accept the change I suggested in the previous message? I already implemented it in libcanard (and updated the tests, too). |
Yeah. Obviously reality trumps specifications. If this encoding isn't consistent in a significant population of hardware then we have to account for that. I can try setting |
We build libcanard in different modes (four to be exact: c99/c11 * amd64/x86), I think Nunavut's verification suite would benefit from that, too. |
I guess just to confirm, how do we want to handle the buffer overflow case? Are we propogating the error up and out of the serialization functions? With the current return types and the way everything's written on the deserialization side we just silently fail. |
Yea. I think that's what I'd do. |
Looking good @davidlenfesty but I'm getting "rc not used" errors again. Can we get rid of the whole "rc" thing and just use this pattern instead:
The block scope will keep this from conflicting with other declarations of "result" and you still get a simpler experience when stepping through with a debugger then you would if we put the function call into the if clause. The exact same instructions for |
Previously these were declared at the top of the functions when needed, which added unnecessary logic and extra surface area for errors (i.e. rc is missing or rc is unused)
I took the liberty of doing the same with Unfortunately I can't compile |
This PR is getting so old and long that I'm inclined to commit it to a feature branch and continue development for there. @pavel-kirienko what do you thing? |
Sure.
…On Wed, Oct 21, 2020, 04:43 Scott Dixon ***@***.***> wrote:
This PR is getting so old and long that I'm inclined to commit it to a
feature branch and continue development for there. @pavel-kirienko
<https://github.com/pavel-kirienko> what do you thing?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#115 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAZFIZA756JNAGJRWXXVTJDSLY4DVANCNFSM4OQWADDA>
.
|
@davidlenfesty I want to look at the compilation error on test_support.c but I can't get a grip on the mechanism to generate the C test code. Could you provide me some basic steps/info to reproduce? |
@PetervdPerk-NXP See this thread Note you'll want to target
|
Okay, this PR has gotten too old and big. I've created a feature branch to work off of here: https://github.com/UAVCAN/nunavut/tree/issue/115 @davidlenfesty and @PetervdPerk-NXP , I'm going to close this PR. Please submit all subsequent progress on #115 as PRs against the issue/115 branch. Thank you all for your contributions. |
You can get a read-made toolchain using docker if you like:
If you want to use your local tools then just get any modern GCC or Clang compiler toolchain and python3.7 or newer. You can see the verification build instructions in this file: https://github.com/UAVCAN/nunavut/blob/issue/115/.buildkite/verify.sh for the c verification build this does:
you can also do
to see a list of targets. Finally, we provide some help with using vscode for native, visual debugging here Let me know if you need more help @PetervdPerk-NXP . |
Thank you both for the explanation @davidlenfesty @thirtytwobits. I've got the docker running and can run the buildkite commands listed above. However cmake fails on a assertion of the public_regulated_data_types
@davidlenfesty could it be you're using a different commit of the public_regulated_data_types? |
No, this is the broken state of this change after the pydsdl update. Sorry, I'm on this one... |
Notes about code:
Notes about PR:
As of creating this, I believe I am mostly done serialization support. There are a few fixes and cleanups to do, but the bulk of it is there. (probably some edge cases I missed too).
Deserialization support is next, and I will likely finish that tomorrow.
I will take this PR out of Draft mode once it is in a state I believe should start getting in depth reviews (I will also clean up the commits a bit so you aren't looking through 20 random commits).