Author Topic: How to plan/structure interrupt-based code?  (Read 44808 times)

0 Members and 5 Guests are viewing this topic.

Offline Kalvin

  • Super Contributor
  • ***
  • Posts: 2178
  • Country: fi
  • Embedded SW/HW.
Re: How to plan/structure interrupt-based code?
« Reply #50 on: September 21, 2020, 07:33:27 pm »
You have temporal couplings between different interrupts and interrupts <-> main() context anyway.

This depends on how many flaws there are your software design. In many cases, it can be avoided. An obvious exception is when your application requires such extremely tight timing that you just cannot decouple things because the timing would be influenced by that.

Quote
A polling-only architecture (a cooperative scheduler) is deterministic and safe if the actual application is written with requisite care (again as it would be anyway with interrupts).

That is a null statement. "Your system will work if you do everything right." Aha.

But seriously:

(a) From the viewpoint of determinism and indeterminism, a cooperative scheduler is a very problematic thing, because it only has very little control about scheduling (that's what "cooperative" means), and a scheduler that only has little control isn't a great help when it comes to achieving safety and determinism. To be exact: a cooperative scheduler only has control over the ORDER of tasks, but not over the TIMING of tasks. But timing is a fundamental and crucial property of safety-critical systems. For example, if your ESP (in a car) is not timed exactly right down to a millisecond, then the driver might experience a surprise. So, a cooperative scheduler is actually an old, outdated, primitive and deeply flawed design that only works because the rest of the system is designed with these flaws in mind.

Because systems with cooperative schedulers are extremely unreliable and prone to crashing, such schedulers were banned from non-embedded systems during the 90s. It is ironic that safety-criticial embedded systems, of all things, still use them. I am pretty sure this is purely because of the huge code base and because a redesign is not deemed feasible, but not because it has anything to do with a reliable, solid software design.

Once, I myself worked for a large corporation in a team that was building a new operating system for cars. Because it was a new system, the scheduler was built from the ground up. For starters, in order to achieve safety, it wasn't a cooperative design. (It wasn't my design, by the way. The scheduler was already there when I joined the team.)

(b) Let us assume you have a really deterministic scheduler. Keep in mind that it does not nothing more than -- well, deterministic scheduling. If you use such a scheduler, ok, then your scheduling is determinstic. But that does still NOT mean that the *system itself* is deterministic. Each of your tasks needs to be deterministic, too. And this goes even beyond the timing aspect. That's not just academic blabla, it is very real.

(c) Finally: Determinism neither means that your system works nor that it is safe nor that it is maintainable. It just means that its semantics is defined in a specific way (which I surely shouldn't go into at this point), but that does not equate to "simple" or "easy". It could still be very complex and broken at 1000 dangerous places without anybody knowing it.

I would say that it is very well possible to design a very deterministic and maintainable system using a simple, co-operative, time-triggered scheduler:

https://en.wikipedia.org/wiki/Time-triggered_architecture

Also, your statements that the co-operative schedulers are somehow particularly unreliable and prone to crashing, is not valid either. Probably you have had a bad experience using a poorly implemented scheduler and poor general system architecture, but do not make a general statement based on your bad experience.
« Last Edit: September 21, 2020, 07:35:06 pm by Kalvin »
 

Offline Fixpoint

  • Regular Contributor
  • *
  • Posts: 105
  • Country: de
Re: How to plan/structure interrupt-based code?
« Reply #51 on: September 21, 2020, 08:05:55 pm »
Also, your statements that the co-operative schedulers are somehow particularly unreliable and prone to crashing, is not valid either.

That's not an opinion but follows directly from the definitions. I already explained it in great detail, but ok, I'll say it again: a cooperative scheduler does not control timing and thus there are no timing guarantees whatsoever. It's even worse: not only are there no timing guarantees, there are also no liveness guarantees. That is actually pretty catastrophic in a safety-critical environment. Case closed. Yes, there is nothing more to say.

Your link to the WP article is not meaningful here because time-triggered systems and cooperative schedulers are different things, they are not synonyms.

Quote
Probably you have had a bad experience using a poorly implemented scheduler and poor general system architecture, but do not make a general statement based on your bad experience.

I have never worked with cooperative schedulers, at least not in a professional environment. My only contact with them was with Windows 3.1, and back then I was a kid and neither knew nor cared.
 

Online voltsandjolts

  • Supporter
  • ****
  • Posts: 3870
  • Country: gb
Re: How to plan/structure interrupt-based code?
« Reply #52 on: September 22, 2020, 02:26:59 pm »
That's not an opinion but follows directly from the definitions. I already explained it in great detail, but ok, I'll say it again: a cooperative scheduler does not control timing and thus there are no timing guarantees whatsoever. It's even worse: not only are there no timing guarantees, there are also no liveness guarantees. That is actually pretty catastrophic in a safety-critical environment. Case closed. Yes, there is nothing more to say.

I have worked on several commercial products with extensive safety requirements that have used, in effect, what you would describe as co-operative scheduler / co-routines / round-robin.
Simple, robust, with a small code base that makes for easier third-party safety assessment.
No embedded OS, no RTOS, no preemptive scheduling. Adding any of these does not guarantee timing or 'liveness' either, it does guarantee more headaches for safety assessment.
Of course there are more complex systems which require these features but it is a case of 'horses for courses'.
« Last Edit: September 22, 2020, 02:34:52 pm by voltsandjolts »
 
The following users thanked this post: Howardlong, uer166

Offline Fixpoint

  • Regular Contributor
  • *
  • Posts: 105
  • Country: de
Re: How to plan/structure interrupt-based code?
« Reply #53 on: September 22, 2020, 04:38:51 pm »
I have worked on several commercial products with extensive safety requirements that have used, in effect, what you would describe as co-operative scheduler / co-routines / round-robin.

Of course you have, why not? By the way, I once wrote working software using BASIC. What does that prove? That BASIC works? Of course it does, but that's beside the point.

Quote
Simple, robust, with a small code base that makes for easier third-party safety assessment.
No embedded OS, no RTOS, no preemptive scheduling. [...] it does guarantee more headaches for safety assessment.

A safety assessment may appear "easier" when there are no abstract concepts to understand and analyze, but that's a deceptive simplicity. I am aware that the industry has been doing it this way for decades, but that is actually sad. (And as we now, from time to time we see a spectacular and deadly failure where it definitely could have been avoided if modern design techniques had been applied.)

How good is the engineering process really when it uses outdated methods from 50 years ago while in the meantime far better methods have become available? Shouldn't we have a look at the new methods and use them to make our products safer to use? Especially when the product is simple because then it is also simpler to try out the new stuff?

Please keep in mind that I am not (only) talking theory, I am talking about things that have been in practical use for quite some time now (also in my own professional work, see above). And so, I can be pretty sure that what I am talking about shouldn't cause any headaches for any safety engineer who is only halfway serious about his job.

So, the way this works is basically to design safety into the product, which we may call *systemic* safety engineering in contrast to *case-based* safety engineering.

When we have a modern scheduler at our disposal, we can make use of its safety and liveness guarantees. This part of the software has to be tested and analyzed once per platform (until a new version is released, of course). The actual tasks of the application -- be it ESP of a car or whatever -- then can be plugged into this safety architecture using the well-defined interfaces.

Then, the magic happens when we define safety and liveness properties for those tasks. Those properties can then be formally analyzed using the well-known methods like model checking, static program analysis etc. So, the whole system gets broken down into *architecturally independent* well-analyzable components. Safety assessment gets *way* easier and the results become better both because it is easier and because of support from the formal side.

The interfacing between the components is part of the underlying system architecture and, as I said, needs to be analyzed only once per platform. Using formal methods, it may even be possible to automatically derive a test suite.

In total, our products become safer becasue more defects are found, and I think that is what we should want. However, for this to work, we must accept that the world is evolving and that new, better methods become available. Cooperative scheduling, for that matter, does not help this process but impairs it, that's why I don't want it in a safety-criticial system.
 
The following users thanked this post: enz

Offline Howardlong

  • Super Contributor
  • ***
  • Posts: 5524
  • Country: gb
Re: How to plan/structure interrupt-based code?
« Reply #54 on: September 22, 2020, 06:38:02 pm »

A safety assessment may appear "easier" when there are no abstract concepts to understand and analyze, but that's a deceptive simplicity. I am aware that the industry has been doing it this way for decades, but that is actually sad. (And as we now, from time to time we see a spectacular and deadly failure where it definitely could have been avoided if modern design techniques had been applied.)


"Modern design techniques" like the 737-MAX *cough*?
« Last Edit: September 22, 2020, 06:40:02 pm by Howardlong »
 

Offline Fixpoint

  • Regular Contributor
  • *
  • Posts: 105
  • Country: de
Re: How to plan/structure interrupt-based code?
« Reply #55 on: September 22, 2020, 07:02:53 pm »
"Modern design techniques" like the 737-MAX *cough*?

As far as I know, Boeing used century-old techniques for that, called negligence, deception, and subterfuge.
 

Offline Renate

  • Super Contributor
  • ***
  • Posts: 1810
  • Country: de
    • Renate's Android Page
Re: How to plan/structure interrupt-based code?
« Reply #56 on: September 22, 2020, 07:11:06 pm »
As far as I know, Boeing used century-old techniques for that, called negligence, deception, and subterfuge.
Not being able to flag an easily identifiable sensor failure while on the tarmac certainly counts as that.
"Oh, you wanted to know if your AoA sensors were totally screwed? You have to pay extra for that light bulb."
 

Offline Howardlong

  • Super Contributor
  • ***
  • Posts: 5524
  • Country: gb
Re: How to plan/structure interrupt-based code?
« Reply #57 on: September 22, 2020, 08:04:44 pm »
"Modern design techniques" like the 737-MAX *cough*?

As far as I know, Boeing used century-old techniques for that, called negligence, deception, and subterfuge.

I was being deliberately obtuse, because you were discounting everything in favour of your dogmatic approach that only your modern design techniques should ever be used. In the Boeing case, they are using a modern software hack to fiddle a proven very reliable decades old design.

My point is around the premise of keeping things simple. Putting layer upon layer of abstraction and code means inevitably you have less and less visibility and understanding of the system as a whole, and how it is likely to behave.

Once you're relying only on empirical results, and ignore the details of what's going on under the hood, you increase risk, you don't reduce it. I'd also say that this abstraction encourages people to use software outside of design criteria, because all to often not all use cases are specified.

There's a time and a place for many methods of achieving the same goal. Some are decades old. I am very sceptical when anyone states there's only one way to achieve something in all cases, discounting all other options, particularly when it must be good because it's modern. I bet the Adobe Flash and Perl developers thought that at one time too.

As I mentioned before, I can't see the point of introducing an RTOS for the OP's relatively simple requirements, that to me seems like over engineering, and unnecessarily adds complexity and cost in terms of development and maintenance effort, and certainly more time to upskill for the OP. If you happen to have a standard that says you must use an RTOS, and you have the skills to develop and maintain it, then go right ahead.
 
The following users thanked this post: voltsandjolts

Online dietert1

  • Super Contributor
  • ***
  • Posts: 3022
  • Country: br
    • CADT Homepage
Re: How to plan/structure interrupt-based code?
« Reply #58 on: September 22, 2020, 09:39:07 pm »
Looking into an open source RTOS was meant as an invitation for a beginner to learn certain things and as a path to results. Software engineers need to learn how to use a given library and its API properly. There is no excuse.

Regards, Dieter
 
The following users thanked this post: newbrain

Offline Fixpoint

  • Regular Contributor
  • *
  • Posts: 105
  • Country: de
Re: How to plan/structure interrupt-based code?
« Reply #59 on: September 22, 2020, 10:10:40 pm »
My point is around the premise of keeping things simple. Putting layer upon layer of abstraction and code means inevitably you have less and less visibility and understanding of the system as a whole, and how it is likely to behave.

The belief that you could have such an "understanding of a system as a whole" is the archetypical engineering hubris. Yes, if you have 1000 LOC, then that may work. But (a) applications don't stay that tiny and (b) in many domains, such tiny applications don't even exist. Systems often grow so large and complex that no person could ever have an understanding of the whole system. Even with a small system, say tens of thousands of LOC, you won't have this understanding. Imagine now we have a big system with millions of LOC.

Many components are being contributed by third parties, people join the team, people leave team, etc etc -- no-one knows the "system" and no-one can know. Let's just accept the reality that systems are complex. We cannot talk their complexity away and deceive ourselves by thinking that we "keep it simple". No, we don't. We just add couplings, and couplings make systems *more* complex (you can also model that mathematically, if you are so inclined). Only if we are not familiar with software design techniques, we might find adding couplings simple and intuitive, but that's just because it's the only thing we know.

In the case of the scheduling stuff, the couplings are introduced because each and every task has to keep the timing of the whole system in mind in order not to destroy it. That is wrong on so many levels, I don't even know where to begin.

Quote
Once you're relying only on empirical results, and ignore the details of what's going on under the hood

If I opened the code for a USB driver, I could only understand it if I sat down for a year and digged deep into the USB specification. Of course, I cannot do that because I have a different job to do. So, I have to accept that I do not know the details and *nobody* knows, maybe not even the guys who wrote the driver because also their team consists of more than one person and the USB spec is huge.

And empirical results are a corner stone of software development, it's called testing.

Quote
you increase risk, you don't reduce it.

Well, the only thing I can do right now is tell you that I am talking about things that have been the basis of the IT and software industry since the 1990s (of course really only the fundamentals, many other things have changed since then). You don't increase risk but instead manage complexity. Managing complexity means to *reduce* risk. That doesn't mean that software always works. But if you did it differently, it would never work because it's too big to "just work".

Quote
I'd also say that this abstraction encourages people to use software outside of design criteria, because all to often not all use cases are specified.

I am not sure what you are referring to.

Quote
I am very sceptical when anyone states there's only one way to achieve something in all cases, discounting all other options,

What I say leaves much room for interpretation, I didn't give specifics. I'm leaving everything open. The only thing I don't want to see -- yes, I don't -- is code that looks as if the last 50 years of reason hadn't happened. And that is all too often the case when I look at embedded code. It's just a big hack with vicious couplings everywhere, no design, no architecture, sometimes they don't even care for formatting and readability. That cannot be good for anyone, especially not the users.

If you want something official, have a look into ISO 26262, for instance. They want you to use the methods that I am talking about. And not because they are "modern" but because they are important -- or at least could be if they were being used.
 

Offline Howardlong

  • Super Contributor
  • ***
  • Posts: 5524
  • Country: gb
Re: How to plan/structure interrupt-based code?
« Reply #60 on: September 22, 2020, 10:20:55 pm »
Looking into an open source RTOS was meant as an invitation for a beginner to learn certain things and as a path to results. Software engineers need to learn how to use a given library and its API properly. There is no excuse.

Regards, Dieter

That;s right, but even when it's open source, how often would a programmer dig into the guts to see how it works? Typically libraries and APIs don't give anywhere near enough details to know how they'll behave in different instances, or how they scale for example.

I'm thinking typically of things like dynamic memory usage for example, or, in the case of web based technologies, dependencies on external resources that might not always be there, or indeed work.

That's not to say we shouldn't use third party APIs or libraries, but unquestioningly treating them as just a building block without understanding their behaviour in a given use case is a significant risk. It's rarely a free lunch, that takes time to analyse. You need to weigh that up against rolling your own.

One last point on an RTOS, almost certainly it won't mean you don't need to understand how interrupts work, you'll still need to have an understanding of that, and write those ISRs. The difference is you'll need to write them in the way that's compatible with the RTOS: more work!
« Last Edit: September 22, 2020, 10:51:00 pm by Howardlong »
 

Offline Howardlong

  • Super Contributor
  • ***
  • Posts: 5524
  • Country: gb
Re: How to plan/structure interrupt-based code?
« Reply #61 on: September 22, 2020, 10:46:55 pm »

The belief that you could have such an "understanding of a system as a whole" is the archetypical engineering hubris.

In the case of the OP's project, and in many others, it is definitely the case that the entire system can be understood, and there of plenty of other designs just like that one.

That's not always the case, but that should be the aim, to understand the system as a whole.

Regarding the USB driver example, that's exactly what I've had to do to make it perform, such is the state of certain vendors' offerings. But you're right, I shouldn't have to do that, but it happens.

I think you're looking at this in an overly dogmatic way, with only one tool in your toolbox. I, on the other hand, am saying it's horses for courses, one should aim to keep things simple, and not over complicate things, and in the case of the OP's task, I don't believe an RTOS is relevant.

I wasn't discounting empirical measurements, if you think that then you're projecting. I was saying that the better understanding you have of a system, the better the solution you'll provide: if you're relying on empirical results only, you're already behind the curve of someone who has a deeper understanding.

I didn't bring up the scheduler thing. IMHO, there's no need for a scheduler in the OP's system at all, it's quite a simple design, it's unnecessary in my opinion.

If you want to boast about vintage, I was brought up on paper tape and germanium transistors in the 70s if that helps!

You see I agree that systems should be well architected, but to do that you need to have a good understanding of what you're trying to achieve. If you don't then of course you'll end up with the systems you refer to. There is a reality check here, and that's that there's such a thing as a sales team, and if they're going to get a big sale you can be sure you'll have to hack something on, and that technical debt will rarely go away. The majority of projects I work on for customers aren't the Utopian green field designs you're yearning for.

You're welcome to disagree, I just think your one-size-fits all approach is over prescriptive. By being so dismissive of concepts like keeping it simple, it's almost as if you're trying to make life hard for yourself by adding complexity.
 

Offline emece67

  • Frequent Contributor
  • **
  • !
  • Posts: 614
  • Country: 00
Re: How to plan/structure interrupt-based code?
« Reply #62 on: September 22, 2020, 10:58:53 pm »
.
« Last Edit: August 19, 2022, 03:57:12 pm by emece67 »
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3571
  • Country: ca
Re: How to plan/structure interrupt-based code?
« Reply #63 on: September 23, 2020, 12:52:04 am »
Systems often grow so large and complex that no person could ever have an understanding of the whole system.

That is true. Therefore it is necessary to do something about the complexity. Simplicity has always been a hallmark of good engineering. One old programmer said:

"Fools ignore complexity, professionals deal with complexity, geniuses remove complexity."

Yet, the modern computer science believes that the way to manage complexity is to add more complexity (Just as economic science believes that the solution to the debt problem is more debt, or as a drug addict believes that the solution to his problem is more drugs). This is obviously not true. The best way to deal with complexity, is to reduce it as much as it can be done (but no more than that).

The modern computer science took a wrong turn. Instead of researching how to improve quality of the software being build, the computer science concentrates on the programming process itself and try to create better rules and mechanisms. Hence ever-increasing complexity of the software. Software is getting bloated beyond comprehension, buggy, and unmanageable. By now I lost all hope and I'm convinced that it isn't going to change on my life time.

So, what to do about that? Go with the flow and drag everything into your project whether you need it or not (such as blinking LEDs with RTOS)? Or, may be a newbie who faces his first project should try to avoid extra complexity. Then, perhaps, years later, when he meets real complexity, he will have some skills to deal with it, or, perhaps, even reduce it.

 
The following users thanked this post: voltsandjolts, Wolfram, Howardlong, Siwastaja, NivagSwerdna, 2N3055, Jan Audio

Offline Howardlong

  • Super Contributor
  • ***
  • Posts: 5524
  • Country: gb
Re: How to plan/structure interrupt-based code?
« Reply #64 on: September 23, 2020, 09:09:45 am »
Surely the OP requirements can be fulfilled w/o an RTOS (some tips were given before about possible approaches), but applications tend to evolve towards the more complexity side and maybe soon ideas like virtual timers, or semaphores, or threads or memory pools or... will need to be reinvented. Embracing a well established RTOS will give access to such resources not needing to spend much time building them from scratch. As such tools are on widespread use they can be found in different OSs, so the time invested learning their use may be well recovered in the future.

If in some scenario the RTOS is not the best approach, that does not preclude its use in all other scenarios.

Regarding the cooperative vs. pre-emptive schedulers, AFAIR both free-RTOS and RTX allows one to use any variant as the scheduler.

Of course this is a matter of taste, but I find some hardware drivers and HAL libraries code much more obscure and hard to grasp than that of an RTOS.

Regards.

I'd agree with that in general, especially regarding vendor hardware drivers and HAL libraries. They tend to try to be all things to all men generic approaches... they're inefficient, and can have a very short shelf life when they no longer support newer silicon. The worst offenders IME are code generators. My preference is the bare metal approach when targeting trivial hardware like I2C, SPI etc. For USB and networking, you pretty much have to use vendor libraries, but they frequently need tweaking. Code generators also have very short shelf lives, and don't lend themselves well to tweaking.

My only thought regarding using an RTOS "in case" is that you can't design for unknown requirements. It's not great practice IMHO to over engineer, it costs money and time unnecessarily. If it's for education, sure, fill your boots, but I'd add that this won't preclude the OP from having to implement interrupts. Furthermore, one problem of over engineering like this is that you can make a rod for your own back: IME it definitely encourages scope creep.
 

Offline JojoS

  • Contributor
  • Posts: 10
  • Country: de
  • Software Engineer, Industrial Measuring Gauges
Re: How to plan/structure interrupt-based code?
« Reply #65 on: September 23, 2020, 09:32:10 am »
How about using MBed-os? Its a RTOS without RT :) Means, that you can configure if you want RTOS stuff included or to use it in the bare-metal variant.
With RTOS you have the preemptive scheduler, but at the cost of more RAM/Flash required. So for simple stuff as the intended project, an event based approch is supported by an EventQueue. Of course with the disavantages of a cooperative system, but for managing user inputs and system events it will work also.
Mbed works also a lot with callbacks, and you can throw an event callback into the queue. This avoids to have large dispatcher switch cases for decoding the events.
USBDevices are supported (F1 has been added recently) also, e.g. you can you a USBSerial class and that will simplify a lot.  Another advantage of Mbed-os is that you have an API that is available for a lot of targets, so when the F103 becomes to small, you can simply switch to e.g. a F401/F411. ST has attached importance on pin compatibilty, so it maybe that you can use the other processor without changing the layout (have not checked the named types).
« Last Edit: September 23, 2020, 09:34:07 am by JojoS »
 

Offline emece67

  • Frequent Contributor
  • **
  • !
  • Posts: 614
  • Country: 00
Re: How to plan/structure interrupt-based code?
« Reply #66 on: September 23, 2020, 12:01:41 pm »
.
« Last Edit: August 19, 2022, 03:57:22 pm by emece67 »
 

Offline Cerebus

  • Super Contributor
  • ***
  • Posts: 10576
  • Country: gb
Re: How to plan/structure interrupt-based code?
« Reply #67 on: September 23, 2020, 01:38:23 pm »
mbed is easy to use, although Flash hungry (just checked with a STM32L432kc, 16 KB for blinky with the RT part removed, 25 KB with it). Also, it is too much "high" level and complex for my taste, I feel I'm loosing control with it (I still use it sometimes, though).

But switching to another (supported) target is quite easy, that's true.

Just for the sake of comparison: ChibiOS on an STM32G474, full RTOS, variable frequency blinky in a thread, frequency set from main thread using the timer library for a quadrature encoder, SPI, an 8 digit seven segment driver atop that, serial port driver, streams library, printf library, quite a few 'debug' printfs - 13kb. (Just kicking the tyres on ChibiOS to see if it's worth adopting/persevering with.)
Anybody got a syringe I can use to squeeze the magic smoke back into this?
 

Online dietert1

  • Super Contributor
  • ***
  • Posts: 3022
  • Country: br
    • CADT Homepage
Re: How to plan/structure interrupt-based code?
« Reply #68 on: September 23, 2020, 02:10:43 pm »
The STM "UM1722 - User manual - Developing Applications on STM32Cube with RTOS" is a good introduction.
On the first page (P. 5) they explain that a typical FreeRTOS kernel will have "4K to 9K bytes".

Regards, Dieter
 
The following users thanked this post: petemate

Online voltsandjolts

  • Supporter
  • ****
  • Posts: 3870
  • Country: gb
Re: How to plan/structure interrupt-based code?
« Reply #69 on: September 24, 2020, 08:18:28 am »
I have worked on several commercial products with extensive safety requirements that have used, in effect, what you would describe as co-operative scheduler / co-routines / round-robin.
Of course you have, why not? By the way, I once wrote working software using BASIC. What does that prove? That BASIC works? Of course it does, but that's beside the point.

What does that prove, you ask.
It demonstrates that an industrial explosives detonation system that includes microcontrollers using co-operative scheduling techniques passes a rigorous third-party safety assessment by a well-known defense contractor subsiduary which specialises in missile electronics.
 
The following users thanked this post: newbrain

Online Simon

  • Global Moderator
  • *****
  • Posts: 18914
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: How to plan/structure interrupt-based code?
« Reply #70 on: September 25, 2020, 10:44:34 am »
setup a timer with an interrupt, every time it fires you increment a counter variable. You main loop will check the counter variable and when a certain number is reached you run a function with your periodic tasks. Doing it this way means that you can have several batches of tasks at different intervals. This means that timing can be accurately achieved for things like changing LED's and reading buttons without time wasting delays.

Things like ADC's can run on interrupts. You can write code that will scan through the ADC inputs repeatedly putting the values into variables. Using arrays helps a lot as you can do things like increment a variable that selects the channel and that variable is the index number of your array so the exact same code can run on all channel round in a loop. Interrupts will also help with oversampling as rather than wait around for the ADC to do several conversions it can use interrupts to call on the CPU when needed but otherwise leave the CPU to run your code. Same sort of thing with comms stuff, set up variables that act as buffers so you put data into them and leave the transmit stuff to work with interrupts or you check a variable flag that tells you a new message has arrived.
 

Offline krish2487

  • Frequent Contributor
  • **
  • Posts: 797
  • Country: dk
Re: How to plan/structure interrupt-based code?
« Reply #71 on: September 25, 2020, 01:56:18 pm »
https://en.wikipedia.org/wiki/Law_of_the_instrument
Tl;Dr - When you have a hammer, everything looks like a nail.
Granted, parts of the quoted message will not be relevant, but in essence that is what your argument is.
One magic bullet to solve every issue.

As engineers, we are meant to keep our mind open about having different tools at our disposal for solving different problems.
Co-operative scheduler isnt certainly perfect, neither is a preemptive RTOS nor will some futuristic solution based on learnings of today and tomorrow.
They are all different approaches to solve a problem.

I have worked on several commercial products with extensive safety requirements that have used, in effect, what you would describe as co-operative scheduler / co-routines / round-robin.

Of course you have, why not? By the way, I once wrote working software using BASIC. What does that prove? That BASIC works? Of course it does, but that's beside the point.

Quote
Simple, robust, with a small code base that makes for easier third-party safety assessment.
No embedded OS, no RTOS, no preemptive scheduling. [...] it does guarantee more headaches for safety assessment.

A safety assessment may appear "easier" when there are no abstract concepts to understand and analyze, but that's a deceptive simplicity. I am aware that the industry has been doing it this way for decades, but that is actually sad. (And as we now, from time to time we see a spectacular and deadly failure where it definitely could have been avoided if modern design techniques had been applied.)

How good is the engineering process really when it uses outdated methods from 50 years ago while in the meantime far better methods have become available? Shouldn't we have a look at the new methods and use them to make our products safer to use? Especially when the product is simple because then it is also simpler to try out the new stuff?

Please keep in mind that I am not (only) talking theory, I am talking about things that have been in practical use for quite some time now (also in my own professional work, see above). And so, I can be pretty sure that what I am talking about shouldn't cause any headaches for any safety engineer who is only halfway serious about his job.

So, the way this works is basically to design safety into the product, which we may call *systemic* safety engineering in contrast to *case-based* safety engineering.

When we have a modern scheduler at our disposal, we can make use of its safety and liveness guarantees. This part of the software has to be tested and analyzed once per platform (until a new version is released, of course). The actual tasks of the application -- be it ESP of a car or whatever -- then can be plugged into this safety architecture using the well-defined interfaces.

Then, the magic happens when we define safety and liveness properties for those tasks. Those properties can then be formally analyzed using the well-known methods like model checking, static program analysis etc. So, the whole system gets broken down into *architecturally independent* well-analyzable components. Safety assessment gets *way* easier and the results become better both because it is easier and because of support from the formal side.

The interfacing between the components is part of the underlying system architecture and, as I said, needs to be analyzed only once per platform. Using formal methods, it may even be possible to automatically derive a test suite.

In total, our products become safer becasue more defects are found, and I think that is what we should want. However, for this to work, we must accept that the world is evolving and that new, better methods become available. Cooperative scheduling, for that matter, does not help this process but impairs it, that's why I don't want it in a safety-criticial system.

What you are advocating is here deferring responsibility of the said code. Shifting the onus of understanding a system to depending on the RTOS / scheduler's vendor to do a good job. What if they screw up? You are asking to assume that the eventual inherent complexity of system will inevitable be far to greater than any one individual can understand, so throw the biggest, most complex, totally not required RTOS / scheduler / framework at it - today. Sorry, thats is just poor optimization. In fact premature optimization. Even in cases where the system is far too complex right at inception, there will always be either
1. people
2. documentation
to handle the said complexity.

For someone who looks like a huge proponent of determinism, your statements about systems and processess and engineering are anything but.
Massive amounts of money has been sunk over decades by various companies to do just this, document stuff.
Are companies implementing an appropriate system to document stuff - maybe.
But it is not because tools dont exist, it is due to the people involved.
Quote
The belief that you could have such an "understanding of a system as a whole" is the archetypical engineering hubris. Yes, if you have 1000 LOC, then that may work. But (a) applications don't stay that tiny and (b) in many domains, such tiny applications don't even exist. Systems often grow so large and complex that no person could ever have an understanding of the whole system. Even with a small system, say tens of thousands of LOC, you won't have this understanding. Imagine now we have a big system with millions of LOC.

Many components are being contributed by third parties, people join the team, people leave team, etc etc -- no-one knows the "system" and no-one can know. Let's just accept the reality that systems are complex. We cannot talk their complexity away and deceive ourselves by thinking that we "keep it simple". No, we don't. We just add couplings, and couplings make systems *more* complex (you can also model that mathematically, if you are so inclined). Only if we are not familiar with software design techniques, we might find adding couplings simple and intuitive, but that's just because it's the only thing we know.

In the case of the scheduling stuff, the couplings are introduced because each and every task has to keep the timing of the whole system in mind in order not to destroy it. That is wrong on so many levels, I don't even know where to begin.
« Last Edit: September 25, 2020, 01:58:58 pm by krish2487 »
If god made us in his image,
and we are this stupid
then....
 

Offline Cerebus

  • Super Contributor
  • ***
  • Posts: 10576
  • Country: gb
Re: How to plan/structure interrupt-based code?
« Reply #72 on: September 25, 2020, 02:51:15 pm »
setup a timer with an interrupt, every time it fires you increment a counter variable. You main loop will check the counter variable and when a certain number is reached you run a function with your periodic tasks. Doing it this way means that you can have several batches of tasks at different intervals. This means that timing can be accurately achieved for things like changing LED's and reading buttons without time wasting delays.
[snip]

Sorry Simon, I'm going to pick on you as an example of "The naïvety of the self-taught programmer".

Consider the situation where you do as you say above. Your C code for the main loop might look like this:
Code: [Select]
volatile int counter;
int compare_value;

...

    while (counter != compare_value)    // Wait for the interrupt routine to set 'counter' to our trigger value
    {
        // a little bit of housekeeping code here, not shown
    }
    // task code follows on here

The compiled machine level pseudocode for the main loop would look a bit like this:

Code: [Select]
again:
          MOV    A, counter
          // danger point starts here
          MOV    B, compare_value
          CMP    A, B    // danger point ends with the finished execution of this instruction
          JE    do_task

          // a little bit of housekeeping code here, not shown

          JMP    again
do_task:
          //  task code follows on here

If the interrupt happens in the region covered by the two 'danger point' comments, the value of 'counter' as set by the interrupt routine, and the value of 'counter' as used by the main loop are different - the main loop doesn't 'see' the updated counter because it has already loaded the now out-of-date value into a register. So your main loop will not execute 'do_task' for at least one more main loop wait cycle, or will miss the trigger entirely if there's another counter incrementing interrupt happens before the loop has time to run around again (say because there's some housekeeping code at the top of the loop as here, or the increment interrupts are very frequent). So you end up with timing jitter, or worse, you miss your event entirely.

This kind of error is very common in naïvely written code that uses interrupts. There are strategies for avoiding this class of errors that ought to be known to every computer scientist on the planet, but they're not known by the vast majority of people who've taught themselves to program. Names for some of these strategies include semaphores, 'wait-free locks', mutexes and so on.

This is one example of a whole class of synchronization problems. Other classes go by names like 'deadlock', 'priority inversion' and so on.

There's obviously not space here to fire up a tutorial, either on how to dodge this problem, or on the wider class of synchronization problems that can be caused by interrupt code or indeed any code that shares values with another piece of code where the execution order of those pieces of code is either unpredictable or can happen in parallel. They can fill entire textbooks.

If someone wants to write this kind of code, and to have it operate reliably and correctly, they have to learn about these hazards and learn about the techniques for avoiding them. These problems can be very subtle, and you're unlikely to spot them, or invent solutions on your own - they occupied a significant part of early computer science research and are still an on-going area of research. That implies either going to the right classes, or picking up the right computer science textbooks and reading them. You'll usually find this stuff in computer science textbooks designed to teach the fundamentals of Operating Systems, as solving this class of problems is an intrinsic part of building an Operating System. You won't find it in typical books on embedded programming.
Anybody got a syringe I can use to squeeze the magic smoke back into this?
 

Online Simon

  • Global Moderator
  • *****
  • Posts: 18914
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: How to plan/structure interrupt-based code?
« Reply #73 on: September 25, 2020, 04:00:39 pm »
I use this method where missing a loop would not even matter. I also tend to do a modulus operation and if the remainder is 0 go with the task list so that if i miss one opportunity I take the next one as it works on any multiple. Things like lighting up LED's, measuring how long a button has been pressed for for de-bouncing purposes, things like that, not timing critical stuff and of course a little thought needs to go into how long the task list could take and to ensure enough overhead.
 

Online Simon

  • Global Moderator
  • *****
  • Posts: 18914
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: How to plan/structure interrupt-based code?
« Reply #74 on: September 25, 2020, 04:05:44 pm »
Your interpretation is also not quite what I meant. The counter variable is incremented on each interrupt. All the loop does is test the counter value or it's modulus and as soon as one of the tests passes the appropriate sub routine is called.

There is the risk that if the routine takes too long then slots will be missed but like I say this is not meant for mission critical timing. Other interrupt driven tasks like ADC reads can run around these task lists. Again this is not a timing controlled system, you have no idea when the ADC value was produced you just know that it is the latest available.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

- - -
-->