Author Topic: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)  (Read 32171 times)

0 Members and 1 Guest are viewing this topic.

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26891
  • Country: nl
    • NCT Developments
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #300 on: March 28, 2023, 10:14:14 am »
But what if you want to share a GPIO pin because it has two functions depending on the mode of operation? Think about using a pin to configure and FPGA which after configuration gets a different function.

using rtic you can instantiate shared resources in the init, those resources can be accessed by different tasks. if the task run on the same priority no special care is required; otherwise you have to provide some kind of resouce contention mechanism, mutex or the like (I still have to test the resource sharing part... so take this with a grain of salt)
But that looks like you can do different things with the pin at the same time which is potentially unsafe. But I think I'll need to do more reading first on how you can transfer control over a resource from one task to the other while doing the check at compile time. Or there has to be some runtime mechanism other than writing the code by yourself. Earlier on I already commented about how Linux deal with peripherals; a peripheral (which can also be a single device on an I2C bus) gets locked by a process for as long as it has an open file descriptor for the device. However, such a mechanism also implies the need for some kind of recovery mechanism in case something goes wrong and a process keep locking a device.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline uliano

  • Regular Contributor
  • *
  • Posts: 172
  • Country: it
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #301 on: March 28, 2023, 10:51:17 am »

But that looks like you can do different things with the pin at the same time which is potentially unsafe. But I think I'll need to do more reading first on how you can transfer control over a resource from one task to the other while doing the check at compile time. Or there has to be some runtime mechanism other than writing the code by yourself. Earlier on I already commented about how Linux deal with peripherals; a peripheral (which can also be a single device on an I2C bus) gets locked by a process for as long as it has an open file descriptor for the device. However, such a mechanism also implies the need for some kind of recovery mechanism in case something goes wrong and a process keep locking a device.

rtic is not preemptive, just interrupts and priority levels, so at the same priority it is impossible that the pin is accessed -at the same time-.

At the same priority you get executed after the other interrupt has finished.
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26891
  • Country: nl
    • NCT Developments
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #302 on: March 28, 2023, 12:15:06 pm »

But that looks like you can do different things with the pin at the same time which is potentially unsafe. But I think I'll need to do more reading first on how you can transfer control over a resource from one task to the other while doing the check at compile time. Or there has to be some runtime mechanism other than writing the code by yourself. Earlier on I already commented about how Linux deal with peripherals; a peripheral (which can also be a single device on an I2C bus) gets locked by a process for as long as it has an open file descriptor for the device. However, such a mechanism also implies the need for some kind of recovery mechanism in case something goes wrong and a process keep locking a device.
rtic is not preemptive, just interrupts and priority levels, so at the same priority it is impossible that the pin is accessed -at the same time-.

At the same priority you get executed after the other interrupt has finished.
But I assume there are two concurrent parallel processes that have access to the same pin where each process can do what it wants given the chance. If that is the case, that it is something you might want to avoid. So only process A or process B can access a pin while the process is alive (=an active task whether it gets execution time or not).
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3140
  • Country: ca
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #303 on: March 28, 2023, 01:39:04 pm »
using rtic you can instantiate shared resources in the init, those resources can be accessed by different tasks. if the task run on the same priority no special care is required; otherwise you have to provide some kind of resouce contention mechanism, mutex or the like (I still have to test the resource sharing part... so take this with a grain of salt)

Mutex and ownership seem to perform the same function.

You acquire ownership to get access to the object then you relinquish it so that the other IRQL could get access to it.

Traditional mutex would do the same thing except that the object is imaginary - nobody knows what is the resource being protected by mutex. But that shouldn't be possible in Rust as it must block the access to the resource protected by the mutex to enforce its no-crash guarantees. Otherwise, you can access the pin even if you forgot to acquire the mutex.

You need to give us more details.
 

Offline uliano

  • Regular Contributor
  • *
  • Posts: 172
  • Country: it
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #304 on: March 28, 2023, 01:47:42 pm »

But that looks like you can do different things with the pin at the same time which is potentially unsafe. But I think I'll need to do more reading first on how you can transfer control over a resource from one task to the other while doing the check at compile time. Or there has to be some runtime mechanism other than writing the code by yourself. Earlier on I already commented about how Linux deal with peripherals; a peripheral (which can also be a single device on an I2C bus) gets locked by a process for as long as it has an open file descriptor for the device. However, such a mechanism also implies the need for some kind of recovery mechanism in case something goes wrong and a process keep locking a device.
rtic is not preemptive, just interrupts and priority levels, so at the same priority it is impossible that the pin is accessed -at the same time-.

At the same priority you get executed after the other interrupt has finished.
But I assume there are two concurrent parallel processes that have access to the same pin where each process can do what it wants given the chance. If that is the case, that it is something you might want to avoid. So only process A or process B can access a pin while the process is alive (=an active task whether it gets execution time or not).

If you don't share the pin between tasks nobody is given simultaneous access. I was answering to

Quote
what if you want to share a GPIO pin

It's your choice

What rtic ensures for you is that while a task is accessing the pin no other task at the same priority level can interfere with that in between the operation.

You need to protect your pin from "simultaenous" access only by higher priority tasks
« Last Edit: March 28, 2023, 01:50:39 pm by uliano »
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3140
  • Country: ca
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #305 on: March 28, 2023, 02:59:12 pm »
What rtic ensures for you is that while a task is accessing the pin no other task at the same priority level can interfere with that in between the operation.

You need to protect your pin from "simultaenous" access only by higher priority tasks

Imagine you have a FET fed which uses 300V to drive an inductive load. The inductor current is rising slowly. If it gets too high the inductor will saturate, FET will die violent death taking most of the board along. So, you have a comparator which monitors the inductor current and invokes the highest priority interrupt if the current climbs above certain critical value. The code in the ISR is supposed to turn the FET off and prevent the disaster - a safety feature, so to say.

So, you write this in Rust. The interrupt gets invoked, your code tries to take ownership of the pin, but it cannot because a task at one of the lower IRQLs had the ownership of the pin when the critical interrupt was invoked. Boom!

Here's an example of a very bad bug which wouldn't be possible if there were no pin ownership. Thus, Rust has created a new "class of errors" which didn't exist before.
 
The following users thanked this post: Siwastaja

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26891
  • Country: nl
    • NCT Developments
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #306 on: March 28, 2023, 03:49:44 pm »
What rtic ensures for you is that while a task is accessing the pin no other task at the same priority level can interfere with that in between the operation.

You need to protect your pin from "simultaenous" access only by higher priority tasks

Imagine you have a FET fed which uses 300V to drive an inductive load. The inductor current is rising slowly. If it gets too high the inductor will saturate, FET will die violent death taking most of the board along. So, you have a comparator which monitors the inductor current and invokes the highest priority interrupt if the current climbs above certain critical value. The code in the ISR is supposed to turn the FET off and prevent the disaster - a safety feature, so to say.

So, you write this in Rust. The interrupt gets invoked, your code tries to take ownership of the pin, but it cannot because a task at one of the lower IRQLs had the ownership of the pin when the critical interrupt was invoked. Boom!

Here's an example of a very bad bug which wouldn't be possible if there were no pin ownership. Thus, Rust has created a new "class of errors" which didn't exist before.
You've got this the wrong way around. In your example you can easely have a race condition in which the fet is turned on again after it has been turned off.

The actual use case is when you have 2 processes: the production process and a self-test process. You don't want to be able to run both these processes at the same time. And if they do for some reason, it is -at the minimum- very nice to be able to deny access to the hardware if a process isn't supposed to run.

And this is not some far fetched example. I have systems in the field that deal with a crap load of energy. Before running a self-test on these systems, the modules need to be set to a self-test mode specifically which allows certain conditions which otherwise are strictly forbidden.

That is the reason why I asked about per-process resource locking. It is a handy feature for a language  / framework to support.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Online JPortici

  • Super Contributor
  • ***
  • Posts: 3461
  • Country: it
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #307 on: March 28, 2023, 03:49:58 pm »
What rtic ensures for you is that while a task is accessing the pin no other task at the same priority level can interfere with that in between the operation.

You need to protect your pin from "simultaenous" access only by higher priority tasks

Imagine you have a FET fed which uses 300V to drive an inductive load. The inductor current is rising slowly. If it gets too high the inductor will saturate, FET will die violent death taking most of the board along. So, you have a comparator which monitors the inductor current and invokes the highest priority interrupt if the current climbs above certain critical value. The code in the ISR is supposed to turn the FET off and prevent the disaster - a safety feature, so to say.

So, you write this in Rust. The interrupt gets invoked, your code tries to take ownership of the pin, but it cannot because a task at one of the lower IRQLs had the ownership of the pin when the critical interrupt was invoked. Boom!

Here's an example of a very bad bug which wouldn't be possible if there were no pin ownership. Thus, Rust has created a new "class of errors" which didn't exist before.

can't the pin be part of the task that does both the setting and the monitoring? Since you're doing it in software anyway (I'm of course not discussing the obvious "use the hardware" as in Output Compare with fault inputs, or directly a comparator)
It seems to me it would be a programming error rather than a deficiency in the language (which i know, is the whole point of the discussion, that believing that new languange = no bugs is delusional)
I honestly don't think i ever wrote a firmware in which a pin is controlled by two processes at the same time. Communication peripherals, yes, and that's why we use mutexes. And it's actually a mutex to a task, only one task control the peripheral
« Last Edit: March 28, 2023, 03:52:14 pm by JPortici »
 
The following users thanked this post: nctnico

Offline uliano

  • Regular Contributor
  • *
  • Posts: 172
  • Country: it
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #308 on: March 28, 2023, 04:00:21 pm »
I'm just speculating because, as I said, the contention of shared resources (the link posted a few posts above) is in my (crowded) next thing to read list, but I suppose that the contention between your tasks can be managed by giving the default to the highest priority and just in few instants here and there the lowest priority can gain access to change something, OR the highest priority just sets a flag that is continuously monitored by the lowest priority (not so elegant). In any case, I'm not an EE however I would implement the safety behind the comparator in hardware gaining in speed and therefore safety.

I see that there are difficulties, before finding rtic even initializing stuff in my mind (with very little knowledge of rust) would have required static globals and other horrible things to be able to share. But then there may be other solutions out there, when I finish my tour in rtic I will probably turn to give an eye to Embassy, which, afaik, implements cooperative multitasking (and may not be suited for your usecase).

My point isnt that Rust is perfect for every application, far from that! (for example the rtic API are changing furiously release after release as it is expected for new things in rapid evolution, this has the effect of obsoleting most material and, you know, documentation sucks). My point is that after some (euphemism) effort I've been really impressed by the effectiveness of static analysis and that I will go on digging for a while.
« Last Edit: March 28, 2023, 04:02:11 pm by uliano »
 
The following users thanked this post: karpouzi9

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8168
  • Country: fi
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #309 on: March 28, 2023, 04:08:56 pm »
How do you define "race condition" here? Such details matter. It is impossible to "simultaneously" write a pin in a single-core microcontroller from two places. Writing a pin is always a correct and allowed operation. The only question is, is it functionally correct?

Maybe expanding on NorthGuy's example: there is a self-test code that acquires ownership to the pin. This code does not normally run, but due to a bug, this process is accidentally triggered. It does not actually actively drive the pin to '1' (or maybe does it just once), but just acquires the ownership. Now the high-priority overcurrent ISR fails to get the ownership. If the ISR run bypassing any ownership checking, it would drive the pin '0' and there would still be no race condition as the self-test code running accidentally is not writing the pin back to '1' even if it has the ownership. A small bug turned into a larger bug. Neither type of bug causes memory corruption or some similar undefined behavior - just the "safe" version is functionally incorrect, while the "less safe" is functionally correct, albeit by luck.

I see both ways having issues; the only real solution is to fix the bug of that self-test code accidentally triggering. The "safe language" fix is therefore not a silver bullet.

I'm all for failing gracefully with out-of-bounds accesses instead of stack corruption, etc., This is something a language can help with. Those true C deficiencies I mean: zero-terminated strings, out-of-bounds array accesses, arrays decaying into pointers, we all know the drill.

But failing in a supposedly "more safe" way in a simple logical error which does not cause a total corruption of program state - one needs to prove the way actually is safer.
 
The following users thanked this post: SiliconWizard

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3140
  • Country: ca
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #310 on: March 28, 2023, 04:47:47 pm »
It seems to me it would be a programming error rather than a deficiency in the language (which i know, is the whole point of the discussion, that believing that new languange = no bugs is delusional)

Of course this is a programming error. The situation is totally symmetrical:

In C, you can write out of the bounds of array, or dereference NULL pointer (or do other "unsafe" things). So we need a different language where such errors are impossible. Rust perhaps?

In Rust, you can mess up with ownership in a wrong way (or do other things we haven't discovered yet). So we need a different language where such errors are impossible. C perhaps?

Or, you may take full responsibility for your bugs, and use common sense and rational thinking. IMHO, that will take you much further towards your goals than expecting miracles from languages, libraries, or whatnot.
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26891
  • Country: nl
    • NCT Developments
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #311 on: March 28, 2023, 04:54:04 pm »
How do you define "race condition" here? Such details matter. It is impossible to "simultaneously" write a pin in a single-core microcontroller from two places. Writing a pin is always a correct and allowed operation. The only question is, is it functionally correct?
Simple: you don't know when and where in the execution path the protection interrupt fires. If the protection interrupt fires just before the normal interrupt sets the pin to turn the fet on, then the protection interrupt has no effect. There is no way you can prove correct operation of this particular system. As Jportici already noticed: there shouldn't be 2 processes controlling the same pin (resource) to begin with. But let's not dig too deep into this.

Quote
Maybe expanding on NorthGuy's example: there is a self-test code that acquires ownership to the pin. This code does not normally run, but due to a bug, this process is accidentally triggered. It does not actually actively drive the pin to '1' (or maybe does it just once), but just acquires the ownership.
The thing is that the self-test code should not be able to acquire ownership because the regular task is already running.

What I'm after is resource locking (by process). Not resource sharing!
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3140
  • Country: ca
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #312 on: March 28, 2023, 06:33:07 pm »
Simple: you don't know when and where in the execution path the protection interrupt fires. If the protection interrupt fires just before the normal interrupt sets the pin to turn the fet on, then the protection interrupt has no effect. There is no way you can prove correct operation of this particular system.

You can do something about it. For example, after the "main" process turns the FET on, it can check if there was a fault condition and turn it back off if needed. This is enough to solve the problem in C, but not in Rust.

Or, you can eliminate the race condition by disabling interrupts in the "main" making (fault check)+(FET setting) an atomic operation. This will work in C.  This will work in Rust too (if you are given an ability to disable interrupts).
« Last Edit: March 28, 2023, 06:36:32 pm by NorthGuy »
 

Offline ve7xen

  • Super Contributor
  • ***
  • Posts: 1192
  • Country: ca
    • VE7XEN Blog
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #313 on: March 28, 2023, 06:49:51 pm »
But what if you want to share a GPIO pin because it has two functions depending on the mode of operation? Think about using a pin to configure and FPGA which after configuration gets a different function.

The way embedded-hal is structured is to use typestate programming to ensure that there is always a maximum of one owner of each peripheral / register. The language itself then guarantees that each peripheral/register can only be (mutably) held by one execution flow at the same time, through the borrow checker. It doesn't directly speak to how you then use these singletons in your own code. If you need to 'share' pins among tasks there are a few ways you can accomplish it. You can use a barrier mechanism like a Mutex<RefCell>, though this should generally only be necessary if you need them in interrupt context. You can continue in the spirit of standard Rust programming and the borrow checker, for example in a main loop, each sub-task can borrow the peripherals from main, and implicitly return them when it is done. This pattern is very common in standard Rust as well, as you also need to contend with the borrow checker for your shared state on desktop too (though you can make use of 'heavy' std library solutions like Arc there).

Or, as others have mentioned, you can use a lightweight framework to help with handling shared state, such as rtic, which for most embedded work satisfies the common requirements - but we are stacking more on top of Rust itself to get there.

Quote
Mutex and ownership seem to perform the same function.

Mutexes are runtime guards. Locking them is blocking and they consume resources and can lead to deadlocks. Ownership is guaranteed by static analysis at compile-time, so it's free at runtime. So yeah, you could build a Rust embedded project that encapsulates all the peripherals in Mutex guards and it'd accomplish roughly the same thing, but it would be quite inefficient. Really in any program, embedded or not, if you're using mutexes when you don't have concurrency, you're doing it wrong. Without the borrow checker you wouldn't really want to force access through Mutexes, due to their cost and risks, but if you don't force it, it means unsafe access is possible. Borrow checker guarantees zero-cost safety for the normal case, and you can still use a mutex (or other concurrent primitives like atomic operations) where you need concurrent access to state.

Quote
How do you define "race condition" here? Such details matter. It is impossible to "simultaneously" write a pin in a single-core microcontroller from two places. Writing a pin is always a correct and allowed operation. The only question is, is it functionally correct?

This is not generally true, at least with the abstraction we are using here. Some microcontrollers do have atomic access at the pin level, but many others do not and require read-modify-write semantics that can be interrupted. It is also not always clear/guaranteed that even if it is a single instruction to write to GPIO, that it is in fact an *atomic* instruction. Without a guarantee, you can guess but can't prove who wins if an interrupt fires in the middle of a GPIO write and performs a conflicting write; most modern cores are pipelined at least if not superscalar, and peripherals are connected via a bus that may need arbitration etc. etc..

Quote
So, you write this in Rust. The interrupt gets invoked, your code tries to take ownership of the pin, but it cannot because a task at one of the lower IRQLs had the ownership of the pin when the critical interrupt was invoked. Boom!

Extra care is definitely required when you need preemptive access to peripherals. This is a special case though, and generally the exact sort of thing you *want* to fail (preferably at compile time) and require extra consideration, because even in C this design is fraught with concurrency pitfalls. I think you could accomplish this with critical sections to guard access. In Rust though this idea of a higher priority access to the peripheral 'working by default' just isn't a thing, and you can't bypass the borrow checker. So you wouldn't be able to compile the implementation you describe in the first place. It is safe by default because it just won't let you assume that it works. You'd need to explicitly create some mechanism to share access to this peripheral between the two threads, and that means taking care of concurrency. This does require a different way of thinking about this kind of situation, but it forces you to be aware of it and consider the details, which I don't think is a bad thing.

Quote
What I'm after is resource locking (by process). Not resource sharing!

There is not much that borrow checker can prove about concurrent tasks, so this is a lot to ask of it. In general, for runtime locking you'll need to use runtime primitives like Mutexes to guard access to multiple references to the same resource. It might be an option to pass ownership when the task is first spawned, but this depends on how your task concurrency is working, I don't *think* this is possible in rtic but I haven't looked into it.
« Last Edit: March 28, 2023, 08:32:16 pm by ve7xen »
73 de VE7XEN
He/Him
 
The following users thanked this post: nctnico

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26891
  • Country: nl
    • NCT Developments
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #314 on: March 28, 2023, 06:56:08 pm »
Simple: you don't know when and where in the execution path the protection interrupt fires. If the protection interrupt fires just before the normal interrupt sets the pin to turn the fet on, then the protection interrupt has no effect. There is no way you can prove correct operation of this particular system.

You can do something about it. For example, after the "main" process turns the FET on, it can check if there was a fault condition and turn it back off if needed. This is enough to solve the problem in C, but not in Rust.

Or, you can eliminate the race condition by disabling interrupts in the "main" making (fault check)+(FET setting) an atomic operation. This will work in C.  This will work in Rust too (if you are given an ability to disable interrupts).
I don't care about the technical implementation at this point. What I'm looking for is if there is some way Rust (or another language) can prevent the problem existing in the first place. A problem that can't exist is much better than a fix.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 19468
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #315 on: March 28, 2023, 07:47:47 pm »
What rtic ensures for you is that while a task is accessing the pin no other task at the same priority level can interfere with that in between the operation.

You need to protect your pin from "simultaenous" access only by higher priority tasks

Imagine you have a FET fed which uses 300V to drive an inductive load. The inductor current is rising slowly. If it gets too high the inductor will saturate, FET will die violent death taking most of the board along. So, you have a comparator which monitors the inductor current and invokes the highest priority interrupt if the current climbs above certain critical value. The code in the ISR is supposed to turn the FET off and prevent the disaster - a safety feature, so to say.

So, you write this in Rust. The interrupt gets invoked, your code tries to take ownership of the pin, but it cannot because a task at one of the lower IRQLs had the ownership of the pin when the critical interrupt was invoked. Boom!

Here's an example of a very bad bug which wouldn't be possible if there were no pin ownership. Thus, Rust has created a new "class of errors" which didn't exist before.

The real error there is attempting to guarantee all that in software running on a conventional processor.

Either do it in hardware, or use a processor plus software that states the hard real-time guarantees at compile time. Both are practical.
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14445
  • Country: fr
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #316 on: March 28, 2023, 08:09:50 pm »
What rtic ensures for you is that while a task is accessing the pin no other task at the same priority level can interfere with that in between the operation.

You need to protect your pin from "simultaenous" access only by higher priority tasks

Imagine you have a FET fed which uses 300V to drive an inductive load. The inductor current is rising slowly. If it gets too high the inductor will saturate, FET will die violent death taking most of the board along. So, you have a comparator which monitors the inductor current and invokes the highest priority interrupt if the current climbs above certain critical value. The code in the ISR is supposed to turn the FET off and prevent the disaster - a safety feature, so to say.

So, you write this in Rust. The interrupt gets invoked, your code tries to take ownership of the pin, but it cannot because a task at one of the lower IRQLs had the ownership of the pin when the critical interrupt was invoked. Boom!

Here's an example of a very bad bug which wouldn't be possible if there were no pin ownership. Thus, Rust has created a new "class of errors" which didn't exist before.

The real error there is attempting to guarantee all that in software running on a conventional processor.

Either do it in hardware, or use a processor plus software that states the hard real-time guarantees at compile time. Both are practical.

I have to agree with this. Now of course we can use methods to decrease the probability of an error with common hardware, but we still can't *guarantee* much, and attempts at doing so are, for the most part, going to be vain.

One of the things I think is "wrong" (or at least vain) with Rust or other similar languages is that they focus on trying to guarantee stuff using conventional hardware and software architectures, which are notoriously bad for certain things.

Hard real-time and resource sharing are two such things.

As to a gigantic source of exploits - arbitrary execution of code by overwriting the "stack"'s content - return addresses should never have been stored on the common data stack anyway. This has been one of the most dreadful mistakes in software history and should be banned.

Regarding sharing hardware resources, such as peripherals, I'm personally in favor of architectures in which one peripheral is only accessed from one task. You avoid a large number of problems this way.
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26891
  • Country: nl
    • NCT Developments
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #317 on: March 28, 2023, 08:16:54 pm »
What rtic ensures for you is that while a task is accessing the pin no other task at the same priority level can interfere with that in between the operation.

You need to protect your pin from "simultaenous" access only by higher priority tasks

Imagine you have a FET fed which uses 300V to drive an inductive load. The inductor current is rising slowly. If it gets too high the inductor will saturate, FET will die violent death taking most of the board along. So, you have a comparator which monitors the inductor current and invokes the highest priority interrupt if the current climbs above certain critical value. The code in the ISR is supposed to turn the FET off and prevent the disaster - a safety feature, so to say.

So, you write this in Rust. The interrupt gets invoked, your code tries to take ownership of the pin, but it cannot because a task at one of the lower IRQLs had the ownership of the pin when the critical interrupt was invoked. Boom!

Here's an example of a very bad bug which wouldn't be possible if there were no pin ownership. Thus, Rust has created a new "class of errors" which didn't exist before.

The real error there is attempting to guarantee all that in software running on a conventional processor.

Either do it in hardware, or use a processor plus software that states the hard real-time guarantees at compile time. Both are practical.
I have to agree with this. Now of course we can use methods to decrease the probability of an error with common hardware, but we still can't *guarantee* much, and attempts at doing so are, for the most part, going to be vain.
You are both digging too deep into the given (poor) example.  ;) You have to look at it on a higher level where a software solution is appropriate.

Where safety/reliability is at stake, it can help to add protective layers in both hardware and software where it is sensible to do so. Again, if a certain programming language is more suitable than another, it could help to reduce engineering costs and create a better product in one go.
« Last Edit: March 28, 2023, 08:20:52 pm by nctnico »
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 19468
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #318 on: March 28, 2023, 08:48:19 pm »
What rtic ensures for you is that while a task is accessing the pin no other task at the same priority level can interfere with that in between the operation.

You need to protect your pin from "simultaenous" access only by higher priority tasks

Imagine you have a FET fed which uses 300V to drive an inductive load. The inductor current is rising slowly. If it gets too high the inductor will saturate, FET will die violent death taking most of the board along. So, you have a comparator which monitors the inductor current and invokes the highest priority interrupt if the current climbs above certain critical value. The code in the ISR is supposed to turn the FET off and prevent the disaster - a safety feature, so to say.

So, you write this in Rust. The interrupt gets invoked, your code tries to take ownership of the pin, but it cannot because a task at one of the lower IRQLs had the ownership of the pin when the critical interrupt was invoked. Boom!

Here's an example of a very bad bug which wouldn't be possible if there were no pin ownership. Thus, Rust has created a new "class of errors" which didn't exist before.

The real error there is attempting to guarantee all that in software running on a conventional processor.

Either do it in hardware, or use a processor plus software that states the hard real-time guarantees at compile time. Both are practical.

I have to agree with this. Now of course we can use methods to decrease the probability of an error with common hardware, but we still can't *guarantee* much, and attempts at doing so are, for the most part, going to be vain.

One of the things I think is "wrong" (or at least vain) with Rust or other similar languages is that they focus on trying to guarantee stuff using conventional hardware and software architectures, which are notoriously bad for certain things.

The guarantees given by Rust seem to me to be valuable, and a useful advance over C.

Nobody in their right mind imagines they extend to real time properties. That's why the example being discussed is so inappropriate.
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3140
  • Country: ca
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #319 on: March 28, 2023, 08:49:26 pm »
You'd need to explicitly create some mechanism to share access to this peripheral between the two threads, and that means taking care of concurrency. This does require a different way of thinking about this kind of situation, but it forces you to be aware of it and consider the details, which I don't think is a bad thing.

What kind of mechanism and how would I create it?
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 19468
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #320 on: March 28, 2023, 08:52:20 pm »
What rtic ensures for you is that while a task is accessing the pin no other task at the same priority level can interfere with that in between the operation.

You need to protect your pin from "simultaenous" access only by higher priority tasks

Imagine you have a FET fed which uses 300V to drive an inductive load. The inductor current is rising slowly. If it gets too high the inductor will saturate, FET will die violent death taking most of the board along. So, you have a comparator which monitors the inductor current and invokes the highest priority interrupt if the current climbs above certain critical value. The code in the ISR is supposed to turn the FET off and prevent the disaster - a safety feature, so to say.

So, you write this in Rust. The interrupt gets invoked, your code tries to take ownership of the pin, but it cannot because a task at one of the lower IRQLs had the ownership of the pin when the critical interrupt was invoked. Boom!

Here's an example of a very bad bug which wouldn't be possible if there were no pin ownership. Thus, Rust has created a new "class of errors" which didn't exist before.

The real error there is attempting to guarantee all that in software running on a conventional processor.

Either do it in hardware, or use a processor plus software that states the hard real-time guarantees at compile time. Both are practical.
I have to agree with this. Now of course we can use methods to decrease the probability of an error with common hardware, but we still can't *guarantee* much, and attempts at doing so are, for the most part, going to be vain.
You are both digging too deep into the given (poor) example.  ;) You have to look at it on a higher level where a software solution is appropriate.

Where safety/reliability is at stake, it can help to add protective layers in both hardware and software where it is sensible to do so. Again, if a certain programming language is more suitable than another, it could help to reduce engineering costs and create a better product in one go.

Strictly speaking I'm arguing against discussing NorthGuy's example, because it is about something that is outside the scope of any (or rather almost any) language and processor.
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 19468
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #321 on: March 28, 2023, 08:58:44 pm »
You'd need to explicitly create some mechanism to share access to this peripheral between the two threads, and that means taking care of concurrency. This does require a different way of thinking about this kind of situation, but it forces you to be aware of it and consider the details, which I don't think is a bad thing.

What kind of mechanism and how would I create it?

Either pure hardware or xC running on xCORE. The IDE will inspect the compiled and optimised code and show you the exact number of clock cycles on any of the paths between here and there. No interrupts and no caches to cause jitter, and you dedicate one of many cores to dealing with each "peripheral". In this case the peripheral would be the FET drive output and the comparator input.

Or use a few logic gates, of course.
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14445
  • Country: fr
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #322 on: March 28, 2023, 09:13:01 pm »
What rtic ensures for you is that while a task is accessing the pin no other task at the same priority level can interfere with that in between the operation.

You need to protect your pin from "simultaenous" access only by higher priority tasks

Imagine you have a FET fed which uses 300V to drive an inductive load. The inductor current is rising slowly. If it gets too high the inductor will saturate, FET will die violent death taking most of the board along. So, you have a comparator which monitors the inductor current and invokes the highest priority interrupt if the current climbs above certain critical value. The code in the ISR is supposed to turn the FET off and prevent the disaster - a safety feature, so to say.

So, you write this in Rust. The interrupt gets invoked, your code tries to take ownership of the pin, but it cannot because a task at one of the lower IRQLs had the ownership of the pin when the critical interrupt was invoked. Boom!

Here's an example of a very bad bug which wouldn't be possible if there were no pin ownership. Thus, Rust has created a new "class of errors" which didn't exist before.

The real error there is attempting to guarantee all that in software running on a conventional processor.

Either do it in hardware, or use a processor plus software that states the hard real-time guarantees at compile time. Both are practical.

I have to agree with this. Now of course we can use methods to decrease the probability of an error with common hardware, but we still can't *guarantee* much, and attempts at doing so are, for the most part, going to be vain.

One of the things I think is "wrong" (or at least vain) with Rust or other similar languages is that they focus on trying to guarantee stuff using conventional hardware and software architectures, which are notoriously bad for certain things.

The guarantees given by Rust seem to me to be valuable, and a useful advance over C.

I was extending the ideas a bit here - hence maybe nctnico's reply - but here it is: I do think those are band-aids on flawed approaches.
Sure band-aids can be useful if you have wounds that need fixing.

Nobody in their right mind imagines they extend to real time properties. That's why the example being discussed is so inappropriate.

I don't know what people have on their mind or not.

But I wasn't even only talking about real-time, if you take my whole post.

I included resource sharing into the mix.
So I'll quote myself:
Quote
Regarding sharing hardware resources, such as peripherals, I'm personally in favor of architectures in which one peripheral is only accessed from one task. You avoid a large number of problems this way.

This one doesn't require any specific hardware. It's just a software approach that can even be taken in pretty much any language.
 

Offline ve7xen

  • Super Contributor
  • ***
  • Posts: 1192
  • Country: ca
    • VE7XEN Blog
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #323 on: March 28, 2023, 09:24:24 pm »
What kind of mechanism and how would I create it?
You'd use a different architecture for this, and avoid accessing peripherals in interrupt context. Probably you should just monitor the action inside the task that is doing the switching and already owns the pin. You could also use an AtomicBool (which is Sync and Send, so can have and access references to it in multiple threads) to set a flag that you monitor for this condition in another task. Or better yet, since your contrived example is mostly in hardware anyway, just satisfy it in hardware by having the comparator output force the FET off with a hard pulldown or something rather than relying on the software to do it.

If you really wanted to do it your way anyway, or had a better reason for it, you'd do it with Mutex<RefCell> of some form, probably enforcing that you're in a critical section (interrupts disabled) to access the inner (a normal mutex is not safe in interrupt context since you will deadlock if the lock is held when the interrupt arrives). This pattern is described in the Embedded Rust book https://docs.rust-embedded.org/book/concurrency/
73 de VE7XEN
He/Him
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3140
  • Country: ca
Re: Embedded Rust on microcontrollers (Cortex M, Cortex A, Softcores etc.)
« Reply #324 on: March 28, 2023, 11:19:43 pm »
What kind of mechanism and how would I create it?
You'd use a different architecture for this, and avoid accessing peripherals in interrupt context. Probably you should just monitor the action inside the task that is doing the switching and already owns the pin. You could also use an AtomicBool (which is Sync and Send, so can have and access references to it in multiple threads) to set a flag that you monitor for this condition in another task. Or better yet, since your contrived example is mostly in hardware anyway, just satisfy it in hardware by having the comparator output force the FET off with a hard pulldown or something rather than relying on the software to do it.

I see. So, the ownership is static and never changes. You can access any periphery from only a single IRQL, and there's no way to circumvent this. I've never, not once, had a problem with accesses to certain periphery, and I do access the same periphery from different IRQLs for various reasons. May be there are people who routinely have those problems, but I really doubt so. Thus this ownership feature solves the problem which doesn't exist. On the other hand, it imposes restrictions on what you can do. So, such feature is both useless and harmful.

Your vision of a CPU is very strange.  You say: "monitor the action inside the task", "set a flag that you monitor for this condition in another task". But how can you monitor flags or actions? CPU consecutively executing instructions from memory. To notice the change of a flag, the CPU needs to encounter an instruction that polls for that flag. If CPU executes a different task which doesn't want to yield, such task must be pre-empted first. This is done through a timer interrupt (which is typically 1ms in RTOSes). 1 ms is a very long time by MCU standards. And then the scheduler may give execution to a different task which it thinks needs execution faster. Here goes another 1 ms, and another, and another ...

For example, you want to drive a pin high, then start a timer, get an interrupt from this timer 100 us later and, in the ISR, drive the pin low. Thus you hope to produce a 100 us pulse. Although this is not a good mechanism for producing pulses, this is a good example to demonstrate the time scale. If you relay the processing of the interrupt to the task-switching mechanism (because the ownership doesn't let you use the same pin from the ISR directly), the length of the pulse will be very far from 100 us. May be several ms instead. That's not you want.

Then you need to get creative and think how do you circumvent the static ownership. For example, you may give the ownership of the pin to the interrupt code, then immediately invoke the interrupt (say with 100 ns timer) to drive the pin high and then start the real timer to drive it low. See what happened: the language forced you to change things a bit to satisfy its internal hunger, but didn't provide any benefits in return. You spent time fighting the language instead of concentrating on your direct tasks. Does that make you more productive. I doubt so.

Or, am I misunderstanding the mechanism of the ownership?
 
The following users thanked this post: Siwastaja


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf