Thoughts about Handling of Complexity

Status
Not open for further replies.

cebersp

Well-known member
Hi all,
perhaps it is right to think about handling of complexity.
Teensy hardware is enabling to do rather complex projects due to its big memories and due to it's high speed. The usage of libraries enables to do complex projects in given time and with small knowledge of details. (I am very thankful that they exist!) Actually it is hopefully not necessary to know how these work. I think most customers use them as black boxes.

Let me first introduce my perspective a little bit: I am a somewhat advanced user of microcontrollers for many years. I can at least read assembler and have done some assembler code in the past. I see myself as a hobbyist, who has projects in mind. Cause to take the time to write this, is that I have lost several frustrating days to find out, why my project stopped to work.
I select the microcontroller for a new project with the following in mind:
Will I be able to complete this project? (Hardware, Libraries, Documentation)
Fun of doing it?
Cost of the project?

I have found, that a good way at least for me to deal with complexity of my projects is to use multitasking, multithreading or multiple cores. TeensyThreads is a step in this direction, but it is not really supported.
A) So I would like to suggest, that Teensy should see some sort of Multitasking as part of the core software (and hardware?) in future.

In my recent project, I find, that very powerful libraries try to implement very very many possibilities. It is possible to use different pins and different SPI busses. While this is great in theory, it is impossible to make sure, that all the possible combinations will work. And I am something like too stupid to read the impressing documentation of exFAT library. ( I was not even able to find the mode-parameter for file.open() so I use something from the examples. ) I think, it is not easy to get and give good answers to questions in this forum, because authors do know their own library, but perhaps not the others in detail. And it is a lot of work to describe a project and to read the description.

B) So my second suggestion would be to reduce the number of possibilities by defining one "Reference Setup".
Define which SPI-channel and which pins shall be used to connect SD-card, which to connect displays,... On the software side there seem to exist several file systems. Choose one of these as reference. I think, a file system is to be seen as part of core with Teensy. The benefit would be, that software could be tested well against this setup. And documentation could focus on this setup. This "Reference Setup" must be very visible in the Teensy documentation. Like with all sorts of standardisation there will be complaints of people, who do find it better, to have their different solution. So the answer is, that the "Reference Setup" is guidance for tests of libraries and a solid starting point for user projects. Someone, who has a different idea can swap (or add) his part of the configuration into the Reference Setup and test it. For questions in the forum, the user can write, I use the Reference Setup with the following differences/additions and have this code:....

So now I am curious, for other thoughts and comments.
Best regards, Christof
 
I think of this as being more about Arduino generally, and not specific to Teensy. There are a couple of Arduino-compatible platforms, one is ESP32, that include an RTOS. By default, Arduino programs run in the "base" task and there is no need to deal with the RTOS. There must be some trade-offs in doing this. Paul has said this is not going to happen for Teensy, and I'm sure he has good reasons.

For my applications, I use a very simple cooperative multi-tasking executive. There are just two functions, create_task() and wait(), which does a task switch. yield() is overridden to call wait(). On top of the executive I have timers, mailboxes, and queues. This has been enough and has worked very well for me, on a number of different platforms prior to Teensy. I know there are differing and strong opinions on the need for preemption. If you want to use a preemptive RTOS, you've got TeensyThreads, or you can search this forum for FreeRTOS and ChiBiOS, and you'll find some resources.

Yes, there are a vast array of libraries of differing quality. Documentation is definitely an issue with Arduino generally. It's not like using a platform from a specific manufacturer, like Microchip or NXP or ST. The amazing support from Paul and this community is what keeps me using Teensy. I try to use as few libraries as possible, and I use those included with TeensyDuino whenever possible. The few times I have reported issues or made enhancements, they have been incorporated in the next release. Try doing that with ST!
 
Paul has said this is not going to happen for Teensy, and I'm sure he has good reasons.
That is certainly in the eye of the beholder.
If there was a really good reason, there would be no obstacle to give it. But this reason is obviously "secret" - nobody but Paul knows it. Since even NXP offers an RTOS for these chips, and there is no technical reason, everyone can think up the reason themselves.
 
Since even NXP offers an RTOS for these chips, and there is no technical reason, everyone can think up the reason themselves.

I think you're right it's not purely for technical reasons. NXP and ST and Microchip can provide RTOS with their development tools a lot more easily because they don't have to be concerned about compatibility with Arduino. Would Teensy be less "Arduino-compatible" if the core included an RTOS? It might simply be too much of a departure and imply too much change. I don't understand EventResponder, and I find TeensyThreads to be more complex than necessary, so I'm using a cooperative executive that provides compatibility with what I do on other non-Arduino platforms.
 
Would you say, ESP is not Arduino compatible? You can completely ignore the fact that it has an rtos and upload a simple sketch with the usual "loop()". Saying ESP is not Arduino compatible requires a fair amount of imagination :)
I'm not aware of a official definition of "Arduino compatible".
I don't understand the "Event responder" too. Well, i know what it does... but it's existance is a mistake and opens the door for wrong developments. It's yield() should be a rtos yield().. not a fake of it.

Imagine your PC runs a state machine... :)
 
Would you say, ESP is not Arduino compatible? I'm not aware of a official definition of "Arduino compatible".

No, I'm not saying that, but I really don't know what is the definition of Arduino-compatible either. I think it's possible there are different levels of compatibility, and I know that Paul knows a vast amount more about that than I do. I know that you and others have been contributing for a long time. I'm relatively new to Arduino and Teensy, and it seems like there are many platforms where you get instructions for a Blink program, and after that you're almost on your own. I haven't seen anything close to Teensy in terms of support for Arduino generally.

I don't understand the "Event responder" too. Well, i know what it does... but it's existance is a mistake and opens the door for wrong developments. It's yield() should be a rtos yield()

Yes, because I don't understand EventResponder, I worry that it will get in the way of using an RTOS. I would be very happy if the Teensy cores use yield() in a way compatible with RTOS task switch, particularly for I2C, SPI, and UART.
 
The point is, the core itself and basic things like I2C, SPI etc are not compatible to a rtos.

I think it was just missed until now. The sweet spot to start with it was missed.
Meanwhile every serious library has a "#ifdef ESP...". it took a while, but now it works pretty reliable. Then, it has it own libs, too. If I look for example ad Bodmer's TFT_eSPI lib.. it's as good as the ILI9341_t3 - if not better - with comparable speed and optional DMA and supports a dozen of displays - not just one.
It would be a lot of work to enforce this rtos compatibility for Teensy libs - after the core itself has been adapted. But then there will be a point where you say: "Works!"

I'm just afraid that train has long since left the station. Too late.
 
The point is, the core itself and basic things like I2C, SPI etc are not compatible to a rtos.

Can you explain what you mean? I find that some parts of the Teensy cores use yield() compatible with RTOS. For example SPI, Teensy4 Wire, and Teensy3 serial use yield() in a way that allows a thread to "wait" for completion of some action, and let other tasks run. Wire and Teensy4 serial do not.

EDIT: I see that the Arduino calls yield() from delay(ms), so it's possible there are other places in the core where yield() would be called. It becomes a little difficult to find them. Has there ever been a statement from Arduino or from Paul that delay() and yield() are designed to be compatible with the use of an RTOS?
 
They are not reentrant. Nothing is reentrant on Teensy. And they are blocking.
For example, a simple print is likely to crash or least will do weird things if used by different tasks. To fix that it needs an incredible amount of work.

Edit: Blocking: The CPU waits for something. For example if you do I2C with 100kHz, the 600MHz "Ferrari" Teensy is exactly as fast as Arduino Uno underclocked to 2MHz. Same for SPI etc..



Good night :) Going to watch a film.
 
Last edited:
They are not reentrant. Nothing is reentrant on Teensy. And they are blocking.
For example a simple print is likely to crash or least do weird things if used by different tasks. To fix that needs an incredible amount of work.

Edit: Blocking: The CPU waits for something. For example if you do I2C with 100kHz, the 600MHz "Ferrari" Teensy is exactly as fast as Arduino Uno underclocked to 2MHz. Same for SPI etc...

That’s why I stick with cooperative tasking. Non-reentrant is okay. I can design around it and still get benefits of multi-tasking.
 
That's also the reason why a "slow" ESP can be faster - it is able to do other things during the wait.
Reentrancy: Yes, you know why and how to avoid it. A beginner does not know that - and Arduino is for beginners. We would read 10x the questions in this forum with "Teensy is crashing..why?"
 
No... nobody uses it. I'm not arware of any questions or mentions here - means there can't be many users of it. So it does not hurt to remove it completely.
I’ve used EventResponder. As you know, even if you did then bin my contribution :) It’s poorly documented but has its uses in a non-multitasking environment, so I’d be irritated if it was removed.
 
I worry that it will get in the way of using an RTOS.

Could you explain specifically how you believe EventResponder will get in the way of using an RTOS?

I'm looking for specific technical reason why the EventResponder API could not be implemented in a hypothetical future migration to an RTOS. I am not interested to hear an argument which boils down to "I want an RTOS now and this isn't an RTOS so I don't like it". I'm also not wanting to hear about the non-RTOS code as it exists today, but rather what fundamental aspect of the API or design would not be workable in a theoretical future where massive work is done to rewrite the widely used libraries to fit well into an RTOS environment. What about the API will get in the way of hypothetically someday integrating with an RTOS?
 
Could you explain specifically how you believe EventResponder will get in the way of using an RTOS?

I don't have any such specific technical reason. The concern I express is based on my ignorance of EventResponder. I've spent a little time looking at the code, and my impression is that it provides a framework for enabling many types of events and defining callback functions. My (perhaps unwarranted) worry is that it might somehow conflict with an RTOS due to use of yield(), and not knowing where and how I could run into trouble with own use of yield() for cooperative task switch.
 
I'm asking because EventResponder is on a very long list of software features I want to improve & document. I had actually wanted to do this in late 2020, but nearly all software development got put on hold for about 18 months while PJRC ran short-staffed. Only now I'm starting to catch up. I'll probably be quite a while until I do much with EventResponder.

But when I (eventually) do get to EventResponder, I'm considering paring back some of the features, and probably adding a base class and at least one other implementation with a queue rather than any new event signaled before the receiving code responds causes the old event to be overwritten.

EventResponder's main purpose is very similar to the "mailbox" or "queue" features in RTOS systems, or perhaps FreeRTOS Notifications. Something happens in an interrupt (or high priority task) and you want to send it to be handled later by some other lower priority task (or lower priority interrupt or main program). Part of the idea behind EventResponder was to be exactly the opposite of your concern... a consistent API which could be implemented using RTOS features like a mailbox, so at least in theory libraries using EventResponder could in the future integrate with an RTOS if the non-RTOS EventResponder implementation where replaced by a thin wrapper around RTOS mailboxes or queues or notifications.

Just to be clear, none of this means PJRC is going to officially support any RTOS in the near future. With so many important software projects so incredibly far behind, starting down that path now would be foolhardy. But if there is some specific aspect of the API which we can know now would possibly prevent (distant) future RTOS support, I'm willing to consider changes.
 
EventResponder's main purpose is very similar to the "mailbox" or "queue" features in RTOS systems, or perhaps FreeRTOS Notifications. Something happens in an interrupt (or high priority task) and you want to send it to be handled later by some other lower priority task (or lower priority interrupt or main program). Part of the idea behind EventResponder was to be exactly the opposite of your concern... a consistent API which could be implemented using RTOS features like a mailbox, so at least in theory libraries using EventResponder could in the future integrate with an RTOS if the non-RTOS EventResponder implementation where replaced by a thin wrapper around RTOS mailboxes or queues or notifications.

Do the terms "lower priority task" and "main program" have any meaning in the absence of an RTOS?

But if there is some specific aspect of the API which we can know now would possibly prevent (distant) future RTOS support, I'm willing to consider changes.

I'm very happy with Teensy, and I'm actually doing pretty well with cooperative multi-tasking with the existing core, so I'm not advocating for an RTOS in the near term. For my purposes, though, I would like to see yield() in the core where the processor is waiting for something, such as completion of an I2C or SPI transfer or other action. For example TwoWire::endTransmission() in WireIMXRT (Teensy4) has a while loop with a timeout, and calls yield() at the bottom of the loop. This allows other tasks to run while this one waits. The same function in WireKinetis (Teensy3) does not have a call to yield(). I'd be happy to document the few places where I think an additional call to yield() would be beneficial for someone trying to use an RTOS, and I'd be glad to share what I'm doing with cooperative tasking.
 
Do the terms "lower priority task" and "main program" have any meaning in the absence of an RTOS?

that is how I program the teensy. data driven processing at different priorities, with the main program (aka loop()) running when no other task is executing.
These features makes sense also in absence of a full blown RTOS.
I prefer to follow OCCAMs razor also in this context.
 
I am happy to see, that there is consent in this discussion, that some step in the direction of "some sort" of multitasking system would be of benefit! ((I don't want this thread to go into a side discussion, but: @joepasquariello, I found https://github.com/joepasquariello/minios. Is there a version for Teensy 4 somewhere too?)) (( For EventResponder I have not found something, how to start. Did not search too long.))

At the moment, I don't see the problem -simple-hearted as I am- to bring in "some sort" of multitasking system step by step. At least I do think (assume) now, that TeensyThreads does not interfere with the parts of the libraries, that I use in my project. Of course you have to be careful, that you don't deal with the same resources in different threads. My problem is/was more, that I had strange behaviour in my project and am not dead certain, if there is really no interference. This is one reason for for my second suggestion for a stable starting point.

I am a little bit frightful, that a full blown RTOS or EventResponder - what ever it is- might not be too helpful for "beginners or advanced beginners" because it is too complicated and documentation is not easy to find.
 
Last edited:
I think an rtos would benefit from the 1176 mailboxes. They couldn't be used if an eventresponder interferes.
In principle there could be both - eventresponder and rtos. However, it would be a bad compromise and in sum the bad of both worlds. An rtos has its own messaging system. You can't seriously use both in parallel and say that's good.
And calling eventresponder code in every systick does not make the system faster.


It would be a sign of goodwill if there was a #define that can disable the EventResponder completely. Not only because of the rtos.


Disabling interrupts is evil and a killer for deterministic timing.

When I read in the code something like this:
Code:
// TODO: interrupt disable/enable needed in many places!!!
(source: Eventresponder).
I hope that the TODO will never be made. It looks like it already happened.. i count 6!! places where disableInterrupts() is called. In a single file.



Translated with www.DeepL.com/Translator (free version)
 
Last edited:
Yield only calls EventResponder.

Yes, but you can attach other code to yield (see https://github.com/TeensyUser/doc/wiki/Hooking-into-yield). E.g. tick an encoder readout, tick some buttons, accelstepper etc. The TeensyTimerTool also uses code called from yield to tick its software timers All stuff which needs to be called as often as possible and shouldn't be disturbed by e.g. a long delay().
Unfortunately there is not much documentation (I found none) and I'm missing a straight forward method to attach own code. The method shown in the link above is a bit clumsy and surely not optimal.
 
...
Disabling interrupts is evil and a killer for deterministic timing.

When I read in the code something like this:
Code:
// TODO: interrupt disable/enable needed in many places!!!
(source: Eventresponder).
I hope that the TODO will never be made. It looks like it already happened.. i count 6!! places where disableInterrupts() is called. In a single file.



Translated with www.DeepL.com/Translator (free version)

deterministic timing on a rtos is also limited, not the same as on bare metal. It's off course best to limit the times for disabling interrupts to the minimum.
 
deterministic timing on a rtos is also limited, not the same as on bare metal.

As long nothing disables interrupts, the highest priority one has deterministic timing. Wether it has an rtos or not. The problem is not the OS - it's the Teensy core (and libs)
 
Unfortunately there is not much documentation (I found none) and I'm missing a straight forward method to attach own code. The method shown in the link above is a bit clumsy and surely not optimal.

when needed I simply declared my own yield(). Not sure if original yield is weak, but compiler did not complain.
Edit: yield() is declared weak
 
Last edited:
Status
Not open for further replies.
Back
Top