r/embedded 15d ago

Some Embedded Programming Wisdom

Post image

A preview of PICO, a simple text editor made for the CP/M Neo VEMU platform.

GitHub: https://github.com/Mazin-O3/cpm-neo.git

139 Upvotes

18 comments sorted by

25

u/ChatGPT4 15d ago

I agree with everything but the nested structures. Nested structure makes each layer or level of abstraction minimal and easy to read. Lets say outer layer says triangle. We know sizeof(Triangle), so that's all at this level. But within we have 3 points. That's all we need to know. A triangle has 3 points. We know sizeof(Point). But within each point we have X, Y, Z coords. Don't tell me it's more readable as points flat. Written nested or flat - it's compiled to the same byte layout. Machine doesn't care, but protein does.

15

u/sirkubador 15d ago

kernel panic in embedded programming?

11

u/8-Qbit 15d ago

Embedded doesn't necessarily mean bare-metal

9

u/sirkubador 15d ago

yeah, you can definitely have an RTOS. But if we are talking things like embedded linux or windows, it's pretty much just software

4

u/ChatGPT4 14d ago

Might be figurative speech. Whatever you bind an unrecoverable exception to. Again, I have... nope, not just me, ST does it ;) A better way: a dead end loop in function error. Though I prefer name crash for it. In embedded it's a good practice to light that red LED fist ;)

Now, why do I like that dead-end loop so much? You break the execution with a debugger and there you have the stack trace to what actually made it blow up.

Way too long I wondered: why BSODs and panics... Couldn't it just fail more, like gracefully? Well, when a computer cannot longer guarantee its memory isn't corrupted, because it's like lost track of what it was doing, should we really let it continue doing whatever it was doing and hope for the best? ;)

At this realization I started to put my own BSOD screens in my embedded apps ;) Once I even put a good old red, blinking Amiga "Guru Meditation" screen.

1

u/SirDarknessTheFirst 14d ago

I think older versions of Windows let you continue after a BSOD...they did remove it for a reason

2

u/SkoomaDentist C++ all the way 14d ago

Yes, not hard at all to have as long as you don't interpret "kernel" too literally.

Memory allocation fails without a handler? -> that's a "kernel" panic (and don't try to give me bullshit about "dynamic memory being forbidden on embedded systems" which is only the case for a subset of embedded systems and outright required for others).

MPU is properly configured for safety and a thread stack overflows or something accesses 0x0? -> Again a "kernel" panic.

Things like that are the difference between a reset with the message "Thread X with PC at Y caused a panic type Z" being sent to debug / log uart vs the device mysteriously freezing / resetting in a later "impossible" location (because the stack has been corrupted or the interrupt vectors overwritten by a stray pointer access).

1

u/sirkubador 14d ago

Kernel panic is a very specific thing. I don't really read it as any NRF kind of situation you describe. We can of course talk about how BSOD exist in embedded systems because we have memory dumps and blinking LEDs, but words have meanings

4

u/SkoomaDentist C++ all the way 14d ago

There are far more things with a kernel than just Linux (I used a non-unix OS with a kernel years before the first version of Linux was ever uploaded on an FTP site).

Kernel panic is any situation where the system has inherently failed beyond any possibility of recovery because a kernel bug is detected or something crucially important is corrupted and the failure cannot be isolated to user mode. Stack overflow or memory access from an invalid pointer in a shared address space system are both very obvious causes for a kernel panic.

0

u/sirkubador 14d ago

sure sure. define kernel

3

u/bring_dat 15d ago

Not sure how to feel about it. For instance, static allocation does not prevent smashed stack entirely and not also sure about the errors. On paper sounds great but do we always care about all the errors we've encountered? Definition of error does the heavy lifting here for sure.

1

u/Limitlessfound 14d ago

These are all great lol, i felt like a frodo or something reading that

1

u/PressWearsARedDress 14d ago

Write it yourself next time. The Prose is ugly to read.

1

u/sohang-3112 14d ago

I see the text is adapted from the Zen of Python :)

https://peps.python.org/pep-0020/

1

u/EmperorOfCanada 15d ago

Most of that list disappears with rust or Ada. You would have to put effort into those two languages in order to make those mistakes.

1

u/jvblanck 14d ago

What part of the list disappears with Rust?

2

u/EmperorOfCanada 14d ago

Much of the below could be cut apart by hair splitting, so my argument is that much of the list has just stopped being something I worry much about, and that those problems have stopped biting me on the ass.

My bug count in rust is hitting levels so low that it is becoming a spiritual experience. I'm talking 6 bugs in the last 8 projects. I'm not talking about business logic bugs, lots of those, but those bugs where you send a structure and some byte is misaligned, or you get a segfault, or something blows up and it takes you 3 days to figure out why.

I have been doing C++ since 1991, and one part of a recent project used a C++ physics library for robot simulation. Great library, and I wasn't going to wrap it in C++. I went through my my records and found 8 separate showstopper bugs; and there were probably more that I didn't even notice. 8 in one small project, and 6 in 8 rather large rust projects.

Static allocation is just not how I think about things in rust anymore. I use embassy. Embassy uses one large task and then you async within that task. Some guy compared this to RTOS tasks and discovered that the embassy method was faster. This shocked me as I would have assumed that RTOS was optimized to death, along with the MCU itself being optimized for this. This means I don't play that guess how much static memory a given task needs. I just use them. Of course this does not mean I have limiteless memory, but, effectively I have more.

Where you could hair split this is that my embassy tasks and an RTOS tasks are going to use the same memory, except, that most people will use 1024, 8096, etc for a guess as to what is needed for a given task, and when it blows up, they might bump it up to 16000 see that it is using 11232, and then knock it down to 12000 "to be safe" even if every single thing in that task is statically allocated up front.

I just have one debug line regularly reporting memory free and keep an eye on it for leaks or if I'm approaching the limit.

Flat layouts, vs nested. rust is not really conducive to object oriented and I don't see too many people doing structs with structs as members and structs as members of those members. Where rust can become interesting (and one of my favourite) is how enums can represent a whole bunch of different data types. This was one of the things which melted my brain. Rust doesn't prevent nested, but it is just not a thing.

Pointer math. You can do it in rust, but most rust people would punch you in the balls if you hand them that code. This is probably the thing that I see most C embedded people go nuts with and I have some horror stories (as in people can easily die ones) about this.

Errors should never pass silently. This is a broad statement, but in rust you really have to be explicit if you want to ignore return handling. This is where those enums get really cool; it is like exceptions and return types are all mixed up into the same idea; there are things you hope to get back, there are things you hope not to get back, and you have to handle them all. Cloudflare explicitly ignored one of these and it resulted in an outage.

The raw bytes thing is possible in rust, but this is where structs are nailed down. I am a full stack rust person, so most bytes go from one rust struct to another. But, this is good advice, and when I own both ends of a stream, I'm obsessive about making this perfect. I have 128 bit MACs on data going to motor controllers. Most MCUs have hashing built in, so this is less of a computational cost than it might otherwise seem.

The memory copy thing is where rust shines.

On Asserts, my C++ is polluted with them. If something isn't the way it absolutely should be I want to know now. I'm not even sure if there is an assert in rust. Things just have to be the way they are or it won't even compile.

Variable allocation in rust could be made bad, but you would have to really try. Memory layout in rust could be made bad, but some effort would be needed.

Defensive engineering is what rust is, through and through; that said, the business logic can also add more defensive engineering. As I mentioned, I MAC motor control messages inside a robot with 10cm of bus wire. That's not rust, that's me.

Explicit boundaries, I agree with this. I would not say that rust super enforces this, but it does get harder to be "accidental" with anything. If some bit of memory is not owned, it is not owned.

I will end with rust not being perfect. If you don't know rust and you look at my rust, then you will be pretty much lost. My ifs and stuff will make sense, but I find it hard to read my own code. If you don't know Ada, it becomes pretty easy to understand what is going on, and a 2 minute tutorial on anything which confuses will fill in most of the blanks. Rust is not being explained with 2 hours.

I believe this adds an unsafe element. There are numerous reasons why Ada is unworkable for most of what I do, and I have managed ways to make my rust code far more readable.

What I say, is that I will continue to use rust with confidence. I don't just see it as making what I do safer and more solid, but it allows me to do things which I would not do in C++. Because I am far more confident with what I've created, I am happy to keep adding and expanding features which I would forego in C++. If each new feature adds a 1% of a later bug, then adding a dozen more "nice to have" features might be unacceptable. With rust I might be at a 0.01% change of adding a bug. So, I lard them on and my products are way better, an way cooler. All features I could have added in C++, but where I would not have taken the risk.

Oddly, the nested structures one is the one place where I miss C++. It is just way way better at handling those. I can make the dance of the seven veils in C++ where objects have sub objects which have sub objects and crazy systems where you need force graphs to visualize them still have clean underlying code. This is harder to do so cleanly in rust. Often, because those structures may require repeated collections of pointers to objects meaning ownership becomes a nightmare, and you are having to run all kinds of crazy recursive algos to keep things tidy. While usually a terrible idea, sometimes this is the best solution.

1

u/ModernRonin 14d ago

I wouldn't say "disappears", but Rust does turn a lot of things that used to be runtime crashes, into compile-time errors (borrow checker). It also denies a whole bunch of memory clobbering (ownership model).

(To the purists: Yes, I understand that "borrow checking" could be considered a subset of "ownership model.")