Fwd: Uavcan in Rust

160 views
Skip to first unread message

Kjetil Kjeka

unread,
Aug 6, 2017, 7:18:51 AM8/6/17
to uav...@googlegroups.com


---------- Forwarded message ---------
From: Kjetil Kjeka <kjeti...@gmail.com>
Date: søn. 6. aug. 2017 kl. 13:14
Subject: Re: Uavcan in Rust
To: Pavel Kirienko <pavel.k...@gmail.com>
Cc: <uav...@googlegroups.com>


Hi Pavel,

Thank you for the thorough response! I'm very excited to hear that both you and other members of the uavcan community have taken interest in Rust. I have cc'd the mailing list as you suggested. Anyone reading this email is, of course, welcome to respond to it.

I'm personally convinced that through giving memory safety guarantees (statically) Rust has a lot to offer for mission-critical embedded software. Rust is also a new language with a somewhat different set of features than traditional embedded languages. I also have high hopes that these features together with cargo (with crates.io integration) will make code reuse a lot less painful for embedded systems. I'm also certain that it will be the best for the community to have all the upstream uavcan repositories organized in one place.

What I'm not certain about is exactly how to structure the uavcan rust project to make it the best it can be. What I'm currently doing is testing out how different ways feels, while finding out what the Rust language has to offer and what features that are about to be implemented. When I've gained some more information and experience I feel like it would be natural to present to the community how I believe the different crates making up the full uavcan implementation should be structured. After improvements have been made and potential issues have been addressed, it would be great if this resulted in the transfer of ownership into the Uavcan team. We should still be able to break the structure (if needed), but not be a high-frequency thing (as it is for the moment).

The current version at crates.io can serialize/parse single frame messages, I hope to have the first usable (being able to parse/serialize any uavcan message) function ready in the next few weeks. Then I will do some lite documentation and write a small post on how to use it. After that, I will need some time to feel out how the current implementation is working and improve upon it (hopefully the community will do some testing as well). This is about when I believe the time is right to try to "finalize" the structure of the implementation. Let's aim to have defined the crate structure and transferred to the uavcan team by the end of September (unless some big changes are required)?

The rest of the email will be me presenting what I'm currently thinking, I encourage everyone to find potential flaws in the reasoning.
uavcan-core (does not require rust std lib): Everything you really need to use uavcan in rust.
uavcan: Implementation of extensions that requires rust std lib (heap allocation etc). I'm not certain about to which extent this will be used but I'm currently thinking that If uavcan-core defines a MessageBuilder trait, it could be implemented as a StaticMessageBuilder (where the space for incoming can frames would be pre allocated) in uavcan-core, while uavcan could have a HeapAllocatingMessageBuilder.

These crates would only care about stuff relevant to uavcan, and would not contain any drivers. A separate crate would be made for every arch port. Some crates could be:
uavcan-stm32: This crate should import uavcan-core and some can driver crate for stm32 and bind them together.
uavcan-socketcan: This crate should import uavcan-core  and socketcan and bind them together
To clarify:
- No assumptions whether std is available or not in the port should be made.
- Everything about the interface should be specified in uavcan-core. This means that the use of the implementation will be uniform over all architectures.
- Even though the ports exist in different crates they may very well exist in the same git repo

The uavcan team can then decide on which implementation is regarded as first class and maintain these. It should also be relatively easy for others to port to second class achritectures. Personally I'm going to atleast port to the S32K serie and socketcan. The S32K drivers need some love first, so it's probably a couple of months down the road before I'm able to release anything useful for the general public. The S32K will also be my secondary testing platform after socketcan.

There are three things I found difficult in libuavcan, and wish to make easier for this implementation. I've also written a short text on how it might be solved. (Disclaimers: I've not used libuavcan much, correct me if I'm wrong).

Building for the correct target
There should be a clear cut between the transport layer and the link layer. This equates into, the implementation should be simple to use without any port. The uavcan core should pump out can frames from uavcan frames regardless whether there exists a can controller or not. If it's simple to use without any port, it will hopefully be simple to port as well. Deciding on which port to use is simply done by selecting the correct cargo crate.

Only using parts of the features 
For a very simple node it might be overkill to use the complete scheduler implementation from libuavcan, if you only wanted to send node status + esc status and only want to receive esc setpoints it should not require several KB with ram. I want this library to be helpful even on the small 8bits.

Using the uavcan structures
I believe that it should be a clear cut between dsdl and uavcan. Not because it's super desirable to use uavcan without dsdl (spoilers: it's not), but rather because of the intuitiveness of how to manipulate the data structures if they're basically rust data structures. I would like to have the uavcan types and structs as first class Rust types.

What I currently see as a desirable way to translate from the dsdl structs into Rust is:
- Since I wish to be able to send any Rust struct over Uavcan (and don't write crazy amounts of code)  to be able to derive the traits needed for serializing/parsing the struct. This is easy in Rust.
- Create a frame from an ID, a path and an Uavcan struct. This is easy with a macro but not as aesthetic as deriving a trait (but not bad at all).
- Use a dsdl compiler to compile from dsdl format to rust struct. Since all code generation is done by the derivable traits this will only require to generate the structs (no implementations)
- I would originally like to calculate the data type signatures when deriving the struct trait, but I think it might end up a lot more practical to do this with the dsdl compiler.

With this, compiling the dsdl is highly independent of the uavcan implementation. Adding rust generation to the python compiler is a tempting way to go. But implementing the compiler in rust also has some advantages. The big advantage of using the python implementation is of course that it will most likely save some time. The most important advantages for reimplementing in rust is:
 - Removing external dependencies (this will ease the use of the library)
 - Better integration with cargo. We could have a setting in Cargo.toml field where the repo/path of the dsdl was specified that defaulted to UAVCAN/dsdl. (this will ease the use of the library, especially with custom dsdl definitions)
 - Rust have great libraries for generating rust code syn/quote
 - Rust also have a library, nom, (that I don't really know but seem great) for constructing parsers.

I'm not in a rush figuring out the details around the dsdl compilation. The important things at this point are that we will need one in a couple of months. It will probably only need to generate structs (and probably signatures). The "code generating" will probably be done by deriving traits and using macros. 

Making the rust library a complete implementation of the full uavcan stack (and it's hopefully future extension to CAN-FD) is definitely one of the goals of the project. I will definitely keep in mind to not exclude redundant interfaces in my design suggestions. I would love to have some help doing this. If this is a particularly difficult thing to make fit in the implementation it would maybe be smart creating an issue tracking this feature so we can discuss what we need from the rest of the implementation to make it happen?

All Best,
Kjetil





lør. 5. aug. 2017 kl. 18:24 skrev Pavel Kirienko <pavel.k...@gmail.com>:
Hi Kjetil!

Sorry about my latency. I didn't want to reduce my answer to a simple OK, yet couldn't find a few minutes to write a coherent response.

The timing of your message couldn't be better, as we at Zubax Robotics are currently considering to start our next embedded project in Rust. It's a very promising technology, and it has a lot of features that I personally identified as sorely missing from C/C++ over my years of involvement with embedded systems. It's peculiar that nowadays so much mission-critical embedded software is being written in inherently unsafe languages. It's about time it should change.

Thank you for the link to the crates, I made sure to subscribe to updates. Would you like to move your repositories over to the UAVCAN team on Github? It would create visibility of your work and attract relevant audiences. In the past, there were some brief talks about writing a Rust implementation of UAVCAN, so having your repository at the UAVCAN organization might attract more contributors.

Concerning the caveats. There are none that come to mind, but I would like to emphasize the importance of the following features:

* Support for redundant interfaces. At the moment this feature is available only in Libuavcan. It might be difficult to get right, but I'm willing to help you here if you need that.

* DSDL --> Rust code generator. This feature is available in Libuavcan and Pyuavcan. I skimmed your code and noticed that you're painfully trying to generate Rust structures from DSDL at compile time, is that so? Are you sure the effort is going to pay off in the end? It might be prudent to choose the ostensibly simpler way of generating Rust structures using a dedicated compiler written in Python, as Libuavcan does. Reusing the same compiler for all implementations would also simplify the extension of the DSDL specification in the future. I'm curious about your motivation for choosing your current approach.

* Easy portability. You mentioned that you're working with S32K144EVB, which is fine. What platforms do you intend to support? How are you going to approach the portability question? Its importance can't be overstated, you understand.

* I would hope to see the Rust library to be as feature rich as Libuavcan. Getting there might be hard, but we're willing to get on board and move the design forward together.

When responding to this email, consider CCing the UAVCAN mailing list please: uav...@googlegroups.com. If not, at least consider announcing your project over there!

Cheers,
Pavel.

P.S. It seems like you're based in Norway! Not exactly next door, but if you ever decided to stop by in Tallinn, make sure to let me know. :D

On Fri, Aug 4, 2017 at 1:40 AM, Kjetil Kjeka <kjeti...@gmail.com> wrote:
Dear Pavel Krienko,

This is just a friendly heads up that I'm currently making an Uavcan implementation for the Rust programming language.

It's currently in the infant step of life, but I've managed to serialize a NodeStatus message and send it over can from the NXP S32K144EVB evaluation board.

Once the implementation has a bit more meat on its bones I intend to do a little write up of how it works and why I feel that Rust is a good fit for Uavcan.

In the meantime, if there are any big caveats I should avoid I will be listening to your advice.

I will add the crates.io link (which you can follow to the github repo as well) if you're curious. But remember that everything is a bit rough around the edges at this point.

Kind Regards,
Kjetil Kjeka


Pavel Kirienko

unread,
Aug 6, 2017, 1:22:39 PM8/6/17
to Kjetil Kjeka, uav...@googlegroups.com
Hi Kjetil,

We would like to supply you with a couple of Zubax Babels plus some cables in order to accelerate the development. Zubax Babels are based on STM32F373CBT6 (ARM Cortex M4F, 128KB Flash, 24 KB RAM, single CAN interface) which is easily hackable. If you find this relevant, please email me your shipping address off the list.

It would perhaps make a lot of sense to eventually write some tutorials targeting Zubax Babel because one of the purposes of that device (alongside being a solid USB-CAN adapter) is to be a general-purpose UAVCAN development board.

The September deadline for the repository transfer is noted. We're open for the transfer at any time though, and actually, I would suggest to do it right now for the reasons of visibility and ease of issue tracking. The fact that the set of repositories might be unstable at first should not be an obstacle.

Concerning the docs, as you surely have noticed, at the moment most of them are centralized at uavcan.org/Implementations. We could add a stub over there indicating that a work on a new implementation is underway for better publicity. Actually, you already have a write access to the website repo, so if you find that proposition meaningful, you can just go ahead and up a pull request.

Your comparison between StaticMessageBuilder and HeapAllocatingMessageBuilder made me wonder whether you're planning to statically pre-allocate memory for each receiver independently, or if you're going to go the way of Libuavcan and Libcanard and implement a shared deterministic constant-complexity fragmentation-free memory allocator. I would like to warn you about the unsustainability of the former approach. The total memory footprint of an implementation that pre-allocates static buffers turns out to be vastly larger than that which uses a shared pool. This has been established empirically in the early days of Libuavcan, which used to have a static buffering feature until at some point it was removed in favor of pool-only allocation.

Observe that Libuavcan uses the pool allocator even when it runs on heap-capable systems such as Linux; we just allocate a single contiguous chunk of memory (about 1 MB large IIRC) and dedicate it all for the pool. The advantage of this approach over plain heap is that we still get to benefit from zero-cost and zero-fragmentation allocation, and additionally, pool allocators are extremely cache-friendly because they tend to re-use the least recently freed areas of memory for new data rather than picking new regions each time (Alexandrescu explored this topic in his "Modern C++ Design"). Although I probably digress too much.

> - No assumptions whether std is available or not in the port should be made.
> - Everything about the interface should be specified in uavcan-core. This means that the use of the implementation will be uniform over all architectures.
> - Even though the ports exist in different crates they may very well exist in the same git repo

Yes, yes, yes.

We currently tend to keep each implementation consolidated in its own repo together with its platform-specific stuff, and we could continue to follow this arrangement, but I'm beginning to suspect that it might not be ideal. The interface between the library and its platform drivers is not supposed to change often, otherwise it's a bad interface bound to make users unhappy. So maybe we should lean towards a dedicated repo for the library itself and one per each platform-specific module? That would also make the re-use of the library cleaner because users won't have to pull a bunch of unrelated code (that of the platform drivers) into their codebases.

> For a very simple node it might be overkill to use the complete scheduler implementation from libuavcan, if you only wanted to send node status + esc status and only want to receive esc setpoints it should not require several KB with ram. I want this library to be helpful even on the small 8bits.

Do we care about 8 bit systems that much? Does it make sense to make another library targeted at low-end systems, given that there's very few of them in the CAN market?

I suggest that we should define a set of core design goals.

When we were going to start working on Libcanard, we put together a design document with Antoine Albertelli et al., have a look: https://github.com/UAVCAN/libcanard/blob/master/DESIGN.md. The first part of it outlines the expectations we had for the end result, which we then used to guide the further development. Do you have a similar set of design goals for the Rust library in mind (by the way, do you have a name for it)?

I would propose the following set of the core design goals, ordered from most important to least important:

- High robustness and safety (hence Rust)
- Feature-completeness, suitability for complex applications (like Libuavcan, unlike Libcanard)
- Ease of use

Please share your thoughts on this.

Concerning the redundant interfaces:

- The platform driver should be able to perform IO multiplexing on more than one interface. In Libuavcan it is handled by uavcan::ICanDriver::select(). Please note that the driver accepts a reference to the frame that the library would like to transmit next, this is very important to avoid the inner priority inversion problem. Unfortunately, I'm yet to find any ready-made embedded CAN driver that would handle the inner priority inversion problem right. All of the drivers that ship with RTOS that I've seen are largely unusable in real applications.

- The library resolves the interface redundancy at the transport layer before the transfers are reassembled. In Libuavcan this is done by a rather messy class known as uavcan::TransferReceiver. I suggest to take its implementation as a reference, I think there's nothing fundamentally wrong with it: https://github.com/UAVCAN/libuavcan/blob/master/libuavcan/include/uavcan/transport/transfer_receiver.hpp#L16

- It would be much, much harder to add the interface redundancy as a feature later, so I propose to have it in mind for the v1.0 milestone.

> I'm not in a rush figuring out the details around the dsdl compilation. The important things at this point are that we will need one in a couple of months. It will probably only need to generate structs (and probably signatures). The "code generating" will probably be done by deriving traits and using macros.

I recognize the advantages of the pure Rust implementation. However, it might make sense to focus on the Python-based approach for now in order to make the library usable faster? You decide.

Thanks!
Pavel.

--
You received this message because you are subscribed to the Google Groups "UAVCAN" group.
To unsubscribe from this group and stop receiving emails from it, send an email to uavcan+unsubscribe@googlegroups.com.
To post to this group, send email to uav...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/uavcan/CAPXD4DTRLw7baKup3AdWNFjMNgDXG3AZgo%3DoN_ETg-WiBkMTuA%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Pavel Kirienko

unread,
Aug 6, 2017, 5:04:11 PM8/6/17
to Kjetil Kjeka, uav...@googlegroups.com
I had an additional idea that I felt like sharing here.

In my previous post I wrote:

> The platform driver should be able to perform IO multiplexing on more than one interface. In Libuavcan it is handled by uavcan::ICanDriver::select(). Please note that the driver accepts a reference to the frame that the library would like to transmit next, this is very important to avoid the inner priority inversion problem.

This is not to say that the select()-based approach is necessarily the preferred one. It has drawbacks.

A couple of weeks ago I was reading up on the RTFM framework by Prof. Per Lindgren: http://rtfm.codeplex.com/. At the end of one of the articles he dispensed some solid observation that stuck with me:

We provide the notion of tasks and critical sections, which by nature is at the very hart of concurrency and resource management, while based libraries and OS-APIs provide mere abstractions to underlying implementations and are by no means natural to concurrency. In -core we reason from the the point of events (or messages), which trigger a reaction (a task or chain of tasks). The thread based solution to this problem, is to create a thread, that in an infinite loops wait for a signal (or set thereof) filters out the specific event, and acts upon this. This event loop programming style, trickles throughout the design, serialises event handling (and in cases re-creates concurrency by deferring computations to "workers"). Even though serialisation and re-creation of concurrency is hidden from the user (by a library) the peep hole for events might be there, creating overhead (queuing, sorting, filtering, dispatching, etc.) and thus crippling responsiveness of the system.

He rightfully points out that embedded applications tend to be reactive. A reactive application implemented on top of the thread-based execution model that modern RTOS provide creates an unnecessary overhead which could be avoided if the RTOS had the direct support for event-based multitasking.

Applying this mindset to Libuavcan one might see that Libuavcan, being a software framework with a reactive API, expects the user to keep one thread blocked in it only for the sake of event dispatching (see the method uavcan::Node<>::spin()). This blocking business goes against the reactive architecture of the library. At some point I realized the shortcomings of the blocking and added an alternative method uavcan::Node<>::spinOnce(), which essentially instructs the library to process the outstanding events and then return immediately without blocking, which maps the reactive nature of the library much better and makes the API more consistent overall.

Applying the above observations to your new implementation, I would like to point out that it might make sense to offload the task of IO multiplexing completely to the application (the user's code), and instruct the user to invoke a designated dispatching method of the library on every event and, additionally, at a maximum fixed interval if there are no events to handle. This approach should ensure the cleanest possible API and full compatibility with three of the most popular usage modes:

- a single-threaded bare-metal environment;
- classic preemptive RTOS with threads;
- low-level reactive schedulers such as https://docs.rs/cortex-m-rtfm

Does that make sense?

Pavel.

Kjetil Kjeka

unread,
Aug 7, 2017, 3:59:28 PM8/7/17
to Pavel Kirienko, uav...@googlegroups.com
Hi Pavel,

I really appreciate the offer for the Zubax Babel. However, I already have a unit. If more hardware units can accelerate the work in the future, I will let you know. Thanks!

It would make sense to target the Zubax Babel, but I'm afraid I won't be able to make this a top priority at this point. Some of my work is currently related around using Uavcan on the S32K series, and this will motivate a lot of testing and dev for this platform. For the same work, we're going to need a CAN-FD extension to Uavcan. When the Rust library is in a lot better shape, I hope to start the discussion on how this extension should be made. Having S32K as a "tier 1" target for this library will help land extensive CAN-FD support early as there are not many MCUs that currently can be used with double redundant CAN-FD interface (Here is one of the end goals for the project). 

If someone in the community would make it a priority to support the Zubax Babel (STM32F373) as a "tier 1" target, this would be perfect. I know Jorge Aparicio test his Rust embedded stuff on the STM32F3. So I suspect the state of Rust support for this MCU is currently better than for any others. If not, I would gladly aim for this target being the third (After S32K and socketcan) to be supported by the Rust library.

In regards to the September deadline, I agree that the visibility point is important. Let's keep it as a hard deadline. In the end of September we transfer no matter what, but let's also try to make it happen sooner. The things I want to get sorted first are:
- In the uavcan package, what conveniences do we hope to get by using the Rust std-lib? I guess the heap might be of limited use if memory allocation is done inside the library, but threads might be useable for something? 
- Personally, I believe there are some things to gained by using std-lib, but maybe I'm wrong. In that case, we should rename uavcan-core to uavcan.
- Right now procedural macros needs to be located in their own crate, should this crate be in the same git repo as uavcan-core (i would think yes?, but it also depends on what the future plans are for proc macros)
- Should uavcan and uavcan-core be in the same git repo (I frequently change my opinion on this subject)
- Should we keep a repo for every port, one repo for all ports or the ports in the same repo as uavcan-core. Like you say, the current solution has been to have everything in the same repo. I don't think this solution would be problematic for Rust, as virtually every user (non-dev) of the library is going to get it from crates.io anyway. But having one repo per port would support the model of having tier 1 repos in the uavcan team and letting everyone implement their own ports as well. If we want to promote an implementation to "tier 1" they could simply transfer the ownership into the uavcan organization (or we could fork it).
- I also want to release v0.0.2 which will be a sort of proof of concept that parses/serialize all uavcan frames, where the uavcan frame can easily be defined as a Rust struct. This is a couple of weeks in the future. If we answer all the other questions by then I guess right after v0.0.2 release would be a good time to transfer. The reason for this is that I'm currently using the "cowboy workflow", I would prefer to rather switch to feature branching after the few features I'm simultaneously working on have landed. The warm reception I've gotten exceeded my expectations, I thought attracting collaborators was further down the road. Right now, I believe that right now this discussion is an as helpful contribution as writing code. 
- If you would prefer to transfer it and keep my cowboy workflow (pushing to master) until v0.0.2, I would be happy to do this once it's decided if we should keep uavcan-core together with uavcan-derive and uavcan.rs
- If we decide to keep uavcan.rs as its own repo I would be happy to transfer this repo immediately (but it's currently practically empty).

Yes, it makes a lot of sense to have something similar to what Libcanard currently have at uavcan.org. I'll make this PR third on my list (after v0.0.2 and transfer of ownership).

I've read through the design document of libcanard and I like it a lot. Putting together something equivalent to the Design goals and feature set should be done at least immediately after v0.0.2 and transfer of ownership, but preferably during. Let's make a branch for putting together this document and a PR tracking the development. I feel like the transfer of ownership and listing on uavcan.org should block the merging of this PR until we're confident that everyone has had a chance to comment. The architecture details should then, with base in the design goals, be settled over a few separate PRs, does this make sense?

That you're bringing up the allocation strategy is great! It's not something I've been able to think enough about yet, and as you say, it's essential to scalability. We should (also as a general rule) try to learn what we can from libuavcan and libcanard (which I'm sure is a lot), but also put the Rust abstractions to best use. I don't know yet what that means, I will need to do a deeper dive into the implementation of these libraries before I'm in a position to discuss it. So don't take what I'm about to write regarding this subject too seriously as it's not well thought out.

Let's consider an RT system running on a resource constrained hardware. It only subscribes to a few uavcan messages, and it only cares about the last transmission that was initiated (old data is useless). It also only listens to one node, where node ID is configured with parameters. For this application, static allocation would have the same memory requirements as the pool allocator. I would argue that for systems where it's essential that it can never miss one of the messages it subscribes to, a static allocator would be the way to assure that (or else how could you guarantee that the allocator had room for more data?).

Ok, then to the other 98% of cases where a pool allocator would be the best solution. The fact that it's possible to use a static allocator must not obscure the ease of use. For instance, if we define a message builder trait, with the implementations of StaticMessageBuilder and AllocatingMessageBuilder, we can make everything generic over the MessageBuilder trait instead of relying on one specified way to build messages. The recommended implementations to use can then be defined through documentation and the Default trait. (I'm not sure how relevant this is, but maybe someone else implements a block allocator we can use? https://doc.rust-lang.org/1.12.0/book/custom-allocators.html)

This is another thing I wish for the library. It should make as few assumptions as possible about the application. However, when this is a conflict of interest with the "ease of use" perspective, it's often the right choice to let it forego. The desire to be useful for 8 bit systems is mainly a desire to not make assumptions about the application. I really think it should be possible to subscribe to a single uavcan message without needing 4k ram, and I currently believe that it's possible to write this library in a way that accommodates for that. I don't think this necessarily should be a core goal of the project, but rather some kind of guideline. I've also used the atmega64m1 (8-bit) for a few projects. Last time with a minimal hacky implementation of uavcan due to libcanard requiring too much ram. The main reason for using this MCU was integrated can in 32 pins package. But I would definitely say that we've reached a point where using these kinds of MCU's in systems using this library is not very common.

I agree on the design goals you've set up. I would like to add the points earlier mentioned.
- Assume as little as possible about the application (right under ease of use)
- Possible to extend for CAN-FD (after Uavcan supports this)

I will need to read up on the libuavcan implementation of redundant interfaces, the section on uavcan.org is a bit thin. Just by guessing, I would think that the logic is concerned with masking duplicates and keeping track of the state of the different CAN-buses. Anything more?

When it comes to dsdl compilation, I would say that the end goal should be a rust implementation. If this is some time in the future, that's totally fine. Unless it's urgent I would like to have a quick look at nom to weigh up the workload vs the gains. However, if someone in the community would want to do this and prefer the Python solution, I would encourage that.

I agree that we should add redundant interface sooner rather than later. In the upcoming days I will make an issue tracking it, we implement it as soon as enough of the infrastructure is in place.

In regards to the "re-creation of concurrency", what you're saying makes a lot of sense. Instead of tackling problems that can be better solved outside the library we should probably focus on making the tools that the library can provide to accommodate to do this.

The email seems to be growing without stop, so I'm going to end with some random comments.
- By expecting the user to keep one thread blocked in the library, we're assuming stuff about the application. This is not ideal.
- When using the system in a bare-metal environment, the reactive solution would be to have the drivers support interrupts. This won't (most likely) work in an RTOS setting.
- When using in an (RT)OS setting, blocking transmit/receive is the simplest interface to use (assuming the driver yield when blocking on hw) is perhaps blocking transmit/receive. This (most likely) won't work in a big while loop setting.
- If the current model (or models) the library support is unsuiteable for an application. This library should still be useful.

Kjetil


To unsubscribe from this group and stop receiving emails from it, send an email to uavcan+un...@googlegroups.com.

Iain Galloway

unread,
Aug 8, 2017, 10:08:21 AM8/8/17
to UAVCAN, pavel.k...@gmail.com
Hello Kjetil, Pavel,
I work for NXP and have been interested in promoting UAVCAN in the drone and small autonomous vehicle space (rovers). 
Please let me know if there is anything i can do to help with S32K.
I am not in a position to collaborate on programming. But if there are questions or hardware needs, i can see what i can do to help.
Regards
-iain

Pavel Kirienko

unread,
Aug 8, 2017, 11:47:49 AM8/8/17
to Iain Galloway, kjeti...@gmail.com, UAVCAN
Hi Kjetil,

> It would make sense to target the Zubax Babel, but I'm afraid I won't be able to make this a top priority at this point. Some of my work is currently related around using Uavcan on the S32K series, and this will motivate a lot of testing and dev for this platform. For the same work, we're going to need a CAN-FD extension to Uavcan. When the Rust library is in a lot better shape, I hope to start the discussion on how this extension should be made. Having S32K as a "tier 1" target for this library will help land extensive CAN-FD support early as there are not many MCUs that currently can be used with double redundant CAN-FD interface (Here is one of the end goals for the project).

This makes sense. Concerning the hardware, see the offer from Iain Galloway, I hope he should be able to assist you with your hardware needs if you have any. Thank you, Iain!

The CAN FD support in UAVCAN is on my list of things to do. It's going to be a trivial addition to the specification; however, implementing it properly in Libuavcan and Libcanard is going to be tricky. Right now this is on hold because none of the hardware we work with currently supports CAN FD; however, you're free to make propositions based on your own experiences, and we'll consider including them in the specification.

> - In the uavcan package, what conveniences do we hope to get by using the Rust std-lib? I guess the heap might be of limited use if memory allocation is done inside the library, but threads might be useable for something?
> - Personally, I believe there are some things to gained by using std-lib, but maybe I'm wrong. In that case, we should rename uavcan-core to uavcan.

My experience with Rust is limited at the moment, so take the following with a big grain of salt. If you looked at how Libuavcan is structured, you would find that there is a platform-agnostic core (the equivalent of your crate uavcan-core), and a bunch of platform-specific drivers that are, essentially, independent libraries. The platform-specific driver for Linux makes heavy use of the C++ standard library with all of the convenient abstractions that come with it: heap allocation, smart pointers, closures, and such.

Additionally, there is a bunch of POSIX-based extensions to the library that are structured as a dedicated platform driver (although they do not really constitute a driver by themselves).

Perhaps it would make sense to arrange the Rust library in a similar manner? Rename uavcan-core into uavcan, and where appropriate, implement more convenient abstractions in the platform drivers?

Concerning the conveniences that can be gained by using the Rust standard library: in C++, the main reason to rely on the standard library in the Linux driver was that the library objects could be allocated in the heap using smart pointers. This makes their usage much, much more convenient because in Libuavcan most of the objects are noncopyable for reasons of memory safety and copious amounts of cross-object referencing that goes on under the hood. I suspect that the same considerations are going to apply in the case of Rust because its memory model is quite similar to that of C++.

> - Should we keep a repo for every port, one repo for all ports or the ports in the same repo as uavcan-core.

I'm leaning towards the option of one-repo-per-port. It seems to offer more flexibility, as you described. Perhaps we should switch Libcanard and Libuavcan to this model as well.

> - If you would prefer to transfer it and keep my cowboy workflow (pushing to master) until v0.0.2, I would be happy to do this once it's decided if we should keep uavcan-core together with uavcan-derive and uavcan.rs

I have no objections to the cowboy workflow on the early stages of development, so this should be no obstacle. You are welcome to initiate the transfer at any point (you can do that via repo settings on Github).

> Let's consider an RT system running on a resource constrained hardware. It only subscribes to a few uavcan messages, and it only cares about the last transmission that was initiated (old data is useless). It also only listens to one node, where node ID is configured with parameters. For this application, static allocation would have the same memory requirements as the pool allocator. I would argue that for systems where it's essential that it can never miss one of the messages it subscribes to, a static allocator would be the way to assure that (or else how could you guarantee that the allocator had room for more data?).

In this scenario, both pool and static allocators offer the same reliability guarantees as long as the pool allocator is sized correctly, so I don't think it makes a lot of sense to support static allocation as an option. The reception logic is somewhat described in the Libcanard design document in the section "RX transfer states", and in the spec in the chapter 4 section "State variables".

You mentioned a use case where the node is restricted to receive data only from one remote node. That is a highly unusual setup, and it doesn't seem to be well compatible with the UAVCAN specification, although the spec doesn't explicitly prohibit that. Usually, a node should not make assumptions about the layout of the bus itself.

In short, I don't think there's a lot to gain from supporting different allocation strategies. As I said, Libuavcan used to offer this feature, but it was removed for the reasons of memory efficiency and simplicity.

> I will need to read up on the libuavcan implementation of redundant interfaces, the section on uavcan.org is a bit thin. Just by guessing, I would think that the logic is concerned with masking duplicates and keeping track of the state of the different CAN-buses. Anything more?

In general, UAVCAN selects an arbitrary interface and works with it, at the same time monitoring the data that comes from redundant interfaces. If the implementation detects that there is newer data available on one of the redundant interfaces, it switches to that interface, and the old one becomes redundant. The pseudo-code provided in the specification (chapter 4 section "State update in a redundant interface configuration") is modeled after the Libuavcan implementation, which has been tested extensively.

Pavel.

To unsubscribe from this group and stop receiving emails from it, send an email to uavcan+unsubscribe@googlegroups.com.

To post to this group, send email to uav...@googlegroups.com.

Kjetil Kjeka

unread,
Dec 28, 2017, 10:15:16 AM12/28/17
to UAVCAN, Pavel Kirienko
Hi everyone,

I've delayed writing an update on the Uavcan.rs project for a bit of time now. The reason for that is that I wanted something that could be evaluated with relative ease by the community before publishing. This ended up taking longer then i expected and since I do not dare to commit to when this "0.0.2" version will be published, i will post a short status update now. I will still do a longer write up when something testable is published.

The current status is:

Serialization/De-serialization:
 - All uavcan data types are representable by using Uavcan.rs. The primitive types (void/int/uint) and arrays have specialized types. Uavcan data types can be represented by using Rust structs and enums (for unions).
 - Serialization/Deserialization methods can be derived for Structs and enums only containing uavcan compatible fields. This means that specialized code for serialization/deserialization will be generated by the Rust preprocessor without any effort.
 - Even though statically defined uavcan types is currently the only possible way to define types compatible with Uavcan.rs a dynamic alternative is also going to be made. This will make it possible to create apps that read dsdl at run time,

DSDL.
 - A full featured dsdl parser is in place.
 - The DSDL compiler have the basic features but a few things needs to be settled before the last features can be implemented.
 - A very simple to use yet highly configurable way to hook into the dsdl definitions is in the making. More specifically, this is a cargo crate that will use the dsdl compiler and build scripts to compile the specified dsdl version.

Driver (driver api).
 - The driver api is currently being experimented with. This is the main cause of 0.0.2 being delayed.
 - The goal is to create something that is "nice" both with and without std-lib use.
 - There exists experimental drivers both for socketcan and s32k144. They are, at the moment, the opposite of user friendly.

Node (user api).
 - The node API is also in the working. The priority is to settle for a minimum API that can be extended later.
 - They are being designed to be easy to use both with and without threading.
 - This simple API is planned to be a part of interface even when more advanced feature are added. It will be there to support a highly resource constrained applications with a minimum set of features.

You can follow the development by looking into issues and PRs at: https://github.com/UAVCAN/uavcan.rs

Contributions are highly appreciated, have a look at the contributions guidelines and issues tagged with "good first issue" to find out where to start.



Reply all
Reply to author
Forward
0 new messages