
Embedded Rules of Thumb (2018) - nsajko
https://embeddedartistry.com/blog/2018/04/26/embedded-rules-of-thumb/
======
PoachedSausage
> Every sensor is a temperature sensor. Some sensors measure other things as
> well.

Some components can become sensors under the right conditions. Example:
Components becoming microphonic.

At high enough frequencies digital design becomes analogue design.

~~~
turbinerneiter
I read a great how-to once where someone described a device which blinks in
the dark - and only in the dark - that uses the LED itself as light sensor.

~~~
PoachedSausage
At work we replace an obsolete diode in part of a circuit in front of a high
gain amplifier. The new equivalent diode came in a glass package unlike the
old one. It caused a bit of head scratching to figure out why the circuit
wasn't behaving as it should and also appeared to be acting as a human
proximity detector.

------
DudeInBasement
Missing the most important Rule of Thumb. This is an embedded device. It has a
_known_ purpose. Every software design decision should be based around this.
This clears up any code goes here (ISR/Task), or 'Use this algo'.

Literally the main point of 'Embedded'.

~~~
froh
Yes and no.

Yes for many embedded microcontrollers.

Not so sure for larger devices with possibly several ECUs and in the field
upgrades. All of the sudden the whole thing becomes a tiny data center with
several nodes, and the architect needs to value some flexibility over clear
cut single purpose.

In automotive, embedded is transformed in this way.

------
barbegal
>Keep ISRs small

I disagree. If you have interrupt priorities then you can treat your interrupt
service routines as the highest priority tasks. As an example lets imagine a
drone controller which has two functions it needs to perform in real time: it
needs to vary the PWM signal to the motor controller and it needs to
acknowledge radio packets. An interrupt is raised when the accelerometer has
new data and another is raised when the radio has received a packet. You could
write ISRs which just queues the accelerometer data and radio packets so they
can be dealt with by a flight control task and a radio task respectively.
Alternatively, you could run the whole of the flight controller inside the
accelerometer ISR. This reduces copying of data and context switches. Because
the accelerometer ISR now takes longer you need to use the radio ISR to
acknowledge the radio packets itself. You split the radio task into two tasks:
a low priority task for processing commands and the ISR task which does the
acknowledgements.

> Avoid blocking function calls

This can be rewritten as never call functions that block on an action of a
lower priority task. It leads to priority inversion in normal tasks and
deadlocks in ISRs.

~~~
nsajko
> I disagree. If you have interrupt priorities then you can treat your
> interrupt service routines as the highest priority tasks. ...

I think "Keep ISRs small" is still a good guideline for most cases. Big
interrupt handlers require more system-wide knowledge to verify (e.g., stack
space, maximal latency, priority problems, ...). They introduce more coupling
into the system, in other words.

Of course, this should not be some hard rule, rather just a rule of thumb. I
wouldn't like it if some policy like this prevented you from choosing the
optimal solution in a case like you suggested.

~~~
barbegal
> Big interrupt handlers require more system-wide knowledge to verify

I think the point I'm trying to make is that sometimes following the rule of
"make your ISR small" actually makes this worse because your bigger high
priority task can be interrupted or you turn interrupts off or mask interrupts
and you end up with even more complexity.

~~~
aidenn0
Sometimes following rules-of-thumb blindly will make things worse. This is one
reason why we call them "rules of thumb" rather than e.g. "laws."

Another example: if you are shipping over a million units, then adding
hardware to simplify software starts to look like a bad trade-off since BOM
starts to dominate NRE.

------
roland35
This is a great list of some basic rules of thumb! Over the years I have
encountered many of these in practice.

I think it is important as engineers to always try and keep an eye on the big
picture. Many times it is easy to get bogged down in an interesting technical
problem in embedded engineering, and we can forget how that problem fits in
with the overall state of the project. One example was I found that I could
not send more than 1 USB bulk packet per millisecond using Texas Instruments'
USB library, even though in theory you could send 19 per millisecond. I wasted
a few days to try and fix that, but it was a waste of time because speeding up
USB only sped up firmware updates which were not a big deal! There are many
examples like this unfortunately.

I think the biggest thing missing from this list is how important it is to
thoughtfully design the hardware to make the firmware easier to design. This
requires a less agile approach, or more flexible prototype hardware until
requirements are better understood. Just one example is using separate SPI/I2C
busses for separate peripherals, even though technically they can be shared.
This helps you not have to worry at all about collisions or managing Direct
Memory Interfaces (DMA).

~~~
astrobe_
I don't think solving the USB issue was a total waste of time. Knowing what is
going on is always valuable.

The "My USB throughput is not what it should be... Oh, well. Must be the USB
cable..." attitude is something that can bite you later, because it can be the
symptom of a bug (e.g. packets lost because of a buggy ISR) that's just
waiting the worst moment to show its ugly face.

~~~
roland35
Yes I did end up learning a lot more about USB bulk mode and the TI drivers so
it wasn't all bad! But there probably were higher priorities...

------
disposedtrolley
With the exception of some of the rules under the Hardware section, this is
some great succinct advice on software development in general :)

~~~
jpm_sd
Well, they're all good rules, but nobody is going to remember them in the form
of a cluttered bulleted list.

Jack Ganssle is quoted repeatedly - his blog has a lot of good war stories
that illustrate many of these points in much greater detail.

[http://www.ganssle.com/blog/index.html](http://www.ganssle.com/blog/index.html)

~~~
disposedtrolley
Agreed, but the intention appears to serve as a launchpad for digging into the
rules yourself. Links to original sources would make this much easier of
course.

Thanks for the link to Ganssle’s blog. I’ll be sure to have a read today.

------
nsajko
One of the things from the list that could be improved is this:

> Algorithmic optimizations have a greater impact than micro optimizations

This is kind of tautological, as micro optimizations can be an important part
of algo optimization. We don't execute algorithms on formal automata, but on
real machines with real architectures, limitations and opportunities.

> * “Real efficiency gains come from changing the order of complexity of the
> algorithm, such as changing from O(N^2) to O(N*logN) complexity”

Only if the constant factor that multiplies the lower-complexity expression is
small enough and N is large enough.

~~~
rightbyte
Ye ... this O() obsession seem to miss the "as n approach infinity" part. It
is just a polynomial and the constant term or which ever can be dominating for
small n. Especially relevant in embedded where searching small static arrays
is a thing.

------
uterm
Thanks for posting this!

As an electronics enthusiast, this really sheds a lot of light on different
aspects of hardware/software development that I hadn't been exposed to before.
I feel like there is a lot of tribal knowledge that people in the business or
going to school for EE stuff are privy to that isn't always out there for the
rest of us, so kudos for aggregating some of that.

along the same line, does anyone have further articles on hardware topics,
like sourcing parts for commercial products? It's another part of electrical
engineering and bringing products to market that I haven't seen much of online
but would love to learn more about.

~~~
HeyLaughingBoy
> sourcing parts for commercial products?

That's a topic onto itself. Is there something specific you're thinking about?

~~~
uterm
For sure, I totally realize those are huge topics, and many people have full
time jobs just focusing one one aspect of that. I guess what I'm really
looking for is a good overview of standard operating procedures and rules of
thumb for purchasing parts and inventory management.

I've seen a lot of really cool hobby projects turned small run commercial
products in the music/synthesizer world and I guess I'm curious how you would
go about sourcing parts for something like that in way that's close to how
it's done professionally.

Also, more specifically what sort of tools do people typically use for
managing inventory? How about comparing prices/vendors? I imagine something
more sophisticated than a simple spreadsheet would be in order and I have to
figure it's different from company to company, but are their any widely
accepted industry standard tool?

~~~
HeyLaughingBoy
For small to mid-level production (singles to mid-thousands per build), most
shops will use a distributor like Digi-Key, Mouser or Arrow to source parts.
Above that, you can start talking to manufacturers directly in some cases if
you can't get good enough prices through distribution.

What throws this off is that a lot of shops these days use Contract
Manufacturers (CM's). Since CMs are building boards for many different shops,
they can purchase parts in larger volumes and get better pricing. They make it
easier to outsource some of your manufacturing since you can just give them a
BOM (Bill of Materials) and your PCB files and get complete, tested boards
back. So you don't even need to deal with parts shopping, etc.

As far as inventory management, if you're not big enough to be using an ERP
(Enterprise Resource Planning) system, it's likely a Quickbooks plug-in, a
spreadsheet or something custom built.

IME, as long as they people can meet their BOM price target, they just buy
from whoever they're used to dealing with.

I have a couple products in low-volume production manufactured in my basement.
90% of my components are purchased from Digi-Key because we're both in
Minnesota (and Digi-Key is the _best_ vendor I've ever dealt with) so most
things I order arrive the next day without having to pay extra. They also have
BOM tools on their site: I can create a bill of materials for a board to make
it easier to order parts: just bring up the BOM and enter a number of units
and it will create a shopping cart for me.

I'll occasionally look around for better prices on some high-cost parts, but
it's hardly worth it at my low volumes: there really isn't enough price
variation on most things.

------
cushychicken
Embedded Artistry's blog is worth a sub if you are in the embedded world, and
aren't reading already.

------
gHosts
> Programs without documentation have no value.

Counterpoint.

Your users won't read the documentation. Get used to it.

Odds on your developers won't either.

Take care when you do read the documentation, it's probably out of date and /
or wrong.

------
bfrog
I feel like half of what's here points to using rust or a rust like language
as a good thing. Even I suppose C++ if you can use the pointer types to help
yourself.

~~~
nsajko
> I feel like half of what's here points to using rust or a rust like language
> as a good thing.

Which points exactly?

Also, neither the Rust standard library, nor the C++ std::shared_ptr were
designed with embedded in mind. Also, Rust is kind of experimental, not really
a good choice for a system that is difficult to update. Not to mention only
Intel and AMD64 platforms have Rust Tier 1 support.

~~~
elwes5
One thing to keep in mind with embedded is size. Some projects you do not get
to have std lib. It just does not fit. This is less and less of an issue as
time goes on. But a few years ago 2k total memory (flash and RAM) was a real
issue you had to conform against. Even that could seem huge for some
platforms.

Depending on the platform Rust or C++ may not be the first one I reached for.

C is pretty compact if you strip the libs out. But usually at that point you
may have to goto ASM just to make it fit.

Had one project which had to be in python (platform dictated by customer). Did
all the right style guide things. Classes, the works. Had to toss all of that
out. As just by making classes subjected it to a 300 byte overhead per object.
I had hundreds of the things. Half my memory was being used by object
management. I needed that space for data. Out goes all the cool by the book
things that seem right to do. Borderline a total re-write. I took those
lessons to the C version. Right up until the new guy decided everything needed
to be C++ and use std:lib and would not listen to me. Suddenly it did not even
fit in flash and RAM even if you use both by about 5x much less any data
needed. He had to spend weeks backing the changes out again. Even after re-
stripping it even then the code size dictated we just could not use some
platforms.

Another thing to keep in mind is compiler maturity. C++ in the past 10 years
has come a long way. But in these embedded platforms you may be working with a
C++ tool-chain from the late 1990s (if you are lucky). Some of the toolchains
shipped with these chips are in poor shape. They will never change unless you
spend a lot of time bringing it up to something semi current. Getting a Rust
tool chain on it? Maybe if you spent months messing around with it. Months
where you could spend shipping product.

~~~
rcxdude
C++ can be written in a compact way: in fact some of it's features are useful
in that regard. But you will probably want to turn off exceptions and RTTI
(two features which are unfortunately not 'zero-cost').

~~~
elwes5
Like you say it can be done in a compact way. There are some gotchas with
memory though. Like you point out RTTI and exceptions. Another one people
forget about is the object overhead themselves. That is not zero cost on many
compilers. There usually is an abstract struct that holds the vtable or
something like one. That is so you can do those cool C++ things like
inheritance and mutability. Most of the time when people say 'c++' I have
found they do not mean the language which is decently compact. They mean the
C++ std lib. It is a distinction many do not make unfortunately.

It is becoming less and less of an issue as the more powerful SoC items have
come down in price and have more memory/flash. The newer ones also usually
come with a somewhat modern compiler stack. Also if you follow some of the
MISRA standards in some projects it can even more radically change what you
can and can not do.

~~~
bfrog
Rust avoids a lot of the C++ pitfalls out of the gate with a split core vs std
library set.

Panic handling and message formatting are something that however is a known
drawback in debug builds. In release builds this gets optimized away in most
cases.

