[Sorry for the delay, I tend not to watch c.a.e anymore (lack of
"interesting" traffic]
On 11/21/2021 6:05 AM, David Brown wrote:
> On 21/11/2021 00:16, Don Y wrote:
>> On 11/20/2021 5:17 AM, David Brown wrote:
>>> In small embedded systems, however, you almost never /need/ dynamic
>>> run-time binding. Your tasks are known at compile-time - as are your
>>> interrupt functions, your state machines, and lots of other things.
>>> Being able to change things at run-time gives you /no/ benefits in
>>> itself, but significant costs in analysability, code safety, and static
>>> checking (plus possibly significant code efficiency costs).
>>
>> Define "small". In the late 70's, early 80's, I was working with
>> "small" 8b devices in 64KB address spaces. ROM and RAM were precious
>> so often in short supply.
>
> Small-systems embedded programming is about dedicated devices with
> dedicated programs, rather than the size of the device.
So, static link a Linux kernel, slap it in a "closed"/dedicated
functionality device (like a DVR?) and it's now "small"?
I consider "small" to be an indication of relative complexity.
Less resources (small) *tends* to be less complex. A "mouse"
is a small system. An MRI scanner isn't. Both can be implemented
however their designers CHOSE to implement them -- none is likely
going to morph into a chess program with the flip of a configuration
switch (so, no *need* to be able to dynamically RElink)!
> <snip war stories>
No, REAL examples of how this was used to advantage. Instead of
baseless claims with nothing to back them up.
>>> No one cares if a source module has to be re-compiled - but we /do/ care
>>> if it has to be changed, and has to go through reviews or testing again.
>>> Some kind of late-binding mechanism can help avoid that at times.
>>
>> If you ware developing for a regulated industry (medical, pharma,
>> aviation, gaming, etc.) you *do* care about having to recompile
>> because you now have a different codebase -- that must be validated.
>
> You care more about the source than the compiled code, but yes, you /do/
> care about having to recompile. However, there is very little
> difference between recompiling one module in a program or many - the
> resulting binary changes and must be tested and qualified appropriately.
There's a huge difference! You only have to test the *component*
that has been modified -- not the entire system!
If you use COTS devices (hardware/software), do you *validate* their
designs? (against what -- a "marketing blurb"?) Are you sure all
the setup and hold times for signals are met (where did you find the
schematics?)? All the calling parameters and return values of
all the internal routines used? (source code availability?)
You likely just "trust" that they've done things "right" and
hope for the best. (after all, what *can* you do to convince
yourself of that?)
When *you* are the creator of those "components", you have access
to all of this information and can *ensure* that your vendor
(i.e., yourself) has produced the product that they claim to
have produced.
I can throw together a product that requires very little final
assembly testing if I've already tested/validated all of the components.
Treating the software that drives a component as part of the component,
FROZEN in silicon (ROM) reduces the size and complexity of the
portions that are yet-to-be-tested (e.g., the application layer)
Did they revalidate the *entire* 737MAX design?? (if you think
they did, then gotta wonder why it took so long to validate it
the *first* time 'round!)
>> OTOH, if you've validated your "display board" against its
>> interface contract, you can use that with any compatible system
>> that *expects* that interface. New display board? Validate *its*
>> code -- and only its code.
>
> We are not talking about separate devices here. The posts are long
> enough without extra side-tracking.
You've missed the point, entirely. Read more carefully:
"Then, probe the memory space for 'ROMs' (typically located on
other cards). Finding a ROM, it would examine a descriptor that
declared entry points ("functions") *in* that ROM before moving on
to locate the next ROM."
There's only one "device". It is implemented using multiple boards
("cards") -- as a good many "devices" are NOT implemented on "single
PCBs". For example:
- One board has the processor and whatever devices seem appropriate.
- Another board has a display (imagine a bunch of 7-segment LEDs
and drive electronics... or PGDs, VFDs, LCDs, etc.) along with
a ROM containing code that knows how to "talk" to the hardware
on *that* board -- BUT NO PROCESSOR.
- Another has the power supply and "power interface" (to monitor
voltage, battery, charger, line frequency, mains power available,
etc.) along with a ROM containing code to talk to the hardware
on *that* board -- BUT, again, NO PROCESSOR.
A "bus" connects them -- so *the* processor on the main board can
interact with the hardware on those cards. And, coincidentally, it
can access the ROM(s) containing the code (that DOES that interaction)!
I.e., the code for the *single* device is scattered across
three boards (in this case). The product glues them together
at run time. Power down the product. Swap out a board with
another that is *functionally* equivalent but potentially
implemented entirely differently (e.g., a different power
supply for a different market; or a different display technology
for use in a different environment) and the product FUNCTIONS
the same as it did before power was removed.
There are *huge* advantages to this approach -- esp in prototyping
new systems. How long do you want to wait to have functional
hardware on which to run your application? If you're buying COTS
modules, you're essentially doing this -- except module X from
vendor A likely won't have *code* on it that will seemlessly interface
with *code* on module Y from vendor B!
They *may* provide you with some "sample code" to assist with your
development. And, I'm *sure* that was written with YOUR needs in
mind? (not!)
I use the same approach in my current project -- except the boards
are *tiny* (~3 sq in) and the ROMs are virtual -- downloaded over
a network interface. So, the *internal* ROM in the processor can be
identical (because "processor boards" are *all* identical!) and, yet,
support any number of add-on boards, with the appropriate software
loaded and configured at run-time (the processor just has to identify
each connected board/module and request associated "firmware" -- in
a manner similar to a kernel "probing" the hardware available in
the environment in which it finds itself executing. Easy to do with
tiny MCUs, nowadays, which can also add "addressable functionality"
(like "ensure the card is powered down on reset -- until the processor
directs you to power it up")
Want to drive a speaker AND a video display? Put a speaker board
and a video display board on a processor, power up, wait for code
to load and you're all set. Want to scatter those functions onto
different "nodes"? Put the speaker board on one processor and the
video board on another. No change to software.
>>>> I use them heavily in my FSM implementations. It lets me encode
>>>> the transition tables into highly compressed forms while still
>>>> retaining a high degree of performance and flexibility.
>>>
>>> Function pointers are certainly often used in such circumstances. I
>>> generally don't use them - because I rarely see compressed forms as
>>> beneficial, they can't be analysed or graphed, you can't follow the code
>>> directly, and you often have /less/ flexibility and significantly less
>>> performance.
>>
>> If you have a few hundred states in an FSM and each state has a dozen
>> or more branches out to other states, the space consumed by the
>> state tables quickly escalates.
>
> Your design is totally and utterly broken, so there is no point in
> pretending there is a "good" way to implement it. Throw it out and
> start again, by dividing the problem into manageable pieces.
Amusing conclusion.
You're suggesting an *application* that is inherently of sufficient
complexity to require hundreds of states to REPRESENT, should NOT be
implemented by EXPOSING those states, the stimuli to which each responds
and the follow-on states? Instead, it should be implemented in some
other, likely LESS OBVIOUS way -- that makes it *easier* to determine
when a particular stimulus is being "unhandled"?
The user interface is the one place in a design where you want to see
EVERYTHING that affects the user. Because the *user* will see everything,
regardless of the "module" in which you try to "hide" it.
"What happens if power fails while he's doing X? What if the backup
battery will only guarantee 5 minutes of continued operation at
that point? What if 30 minutes? How will he know if the 'typical'
operation can likely be completed in the 'remaining battery'?"
Should you bury the device's reaction to these events in the "power
monitor" module? And, maybe have the UI module *talk* to it to tell
it when it is acceptable to commandeer the (one line!) display to
paint a message to inform the user of this event? And, when that
message should be dismissed? Should the "power monitor" module
need to talk to the display module to preserve the previous content
of the display, overwritten by its power fail message and needing
restoration thereafter? What if the previous display contents should
NOT be restored (because the UI doesn't *want* them restored in that
event)?
"What if the user decides he wants to pump Hi-Test instead of Regular
Unleaded... AFTER he's already selected the latter? Is his choice locked
in? Or, is there a means by which he can change his mind, after the fact?
Do we require him to finish the transaction and start a new one? How do
we tell him that?"
Should the module that monitors the fuel selection buttons make/enforce
that restriction? How should they communicate that fact to the UI
module (which can then decide how to communicate it to the user?)
"What if the customer doesn't scan their membership/discount card
at the *start* of the checkout process? Do we still award the
discounts on those 'special-sale-for-members-only' items that
have already been scanned? Or, is the customer SoL? What if he
tries to complete the transaction before doing so -- do we remind
him and give him one last chance? Or, too-bad-so-sad?"
Should the card reader module have direct access to a "membershipID"
variable, somewhere, that it uses to tell the rest of the machine
that the customer has presented a membership card? How do we tell
that card reader module NOT to accept the membership card accept
in certain places in the UI protocol?
FSMs make ALL of these conditions (events) very visible. They
aren't buried in conditionals or switch statements or "between
the semicolons" or interactions between modules, etc. A "power
monitor" module may *determine* that power has failed (using
whatever algorithm is appropriate to *it* -- and of which the
user need not be aware). But, if that *event* is going to
affect the flow of activities/operations that the *user*
experiences, then it has to be made visible to the "process"
that is interacting with the user.
An FSM makes this VERY terse. It simply enumerates states
(which will exist in EVERY implementation you choose because they
are a characteristic of the user interface protocol and not the
implementation, though likely in a less discrete/visible manner)
and events of interest in each of those states. Choose state
names, event names and transition routine names well, and there's
no need to even go digging through the *code*!
I have an annoying capability of being able to walk up to a
product and tickle a yet-undiscovered bug in its user interface.
Because I *know* that folks aren't systematic about how they
express these interfaces. They scatter decisions/algorithms
that are pertinent to the UI in several "must be remembered"
locations in their codebases.
So, while its likely that you verified that the cables that
interconnect the different "boxes" in your design are in place
during POST, I'll wager you've not thought about the fact
that the state of those cables can *change* during operation!
"Ooops! I just unplugged this cable and now your machine
THINKS the world is round but I've suddenly rendered it flat!"
Instead, put the POST in the UI FSM (because it will be interacting
with the user -- if only to allow you to say "Ready for operation")
and, chances are, someone will notice that there is a transition
that reacts to ALL_CABLES_CONNECTED to exit the "POST" state (and
generate that "ready" message on its outbound transition). And,
if they are diligent, they will ask why that "event" is never
present in subsequent operating states.
"Gee, I wonder what will happen if I unplug this cable *now*...
who/what will see that? How? What will they deduce from the
consequence of that *fault*?"
Ask yourself if your tech writer can write a user manual from your
codebase. The "next developer" will be starting maintenance of
your codebase from that level of experience (with THAT product).
Or, do YOU have to effectively tell him what your code is doing,
and when, in order for him to undertake that effort. (Are you
sure you remember EVERYTHING that can happen and how it will
manifest to the user?)
I designed a little "electronic taperule" many years ago. A
"simple" device (in terms of implementation, inherent complexity
and user interactions). Two buttons, one "7 segment" display.
But, all sorts of "modes" (states!) that it can operate in!
Power it on, power it off.
Indicate whether you want to display results in imperial or metric units.
Decimals, or fractions.
"Zero" the rule's readout -- regardless of how far extended the tape
may be at the time ("how much LONGER is this piece of lumber than
THAT piece?")
Flip the display (so you can hold the rule in your other hand)
Battery status.
Auto-power-off.
etc.
I drew a state transition chart and used that to present the design
to management. No technical skills needed (all "financial" people)
yet they could all understand what the device would do and how the
user would KNOW where he was (in the state diagram) as well as how
to navigate to another place in that diagram ("usage mode")
Yeah, its amusing to watch grown men tracing lines from one "state
bubble" to another. But, had I given them a lengthy bit of prose
or some source doe listings, their eyes would have glazed over.
[And, I could overtrace those same lines with a yellow highlighter
to make it apparent how much of the device's operation we'd
already discussed. "What does THIS transition do? It hasn't been
highlighted, yet... Oh, yeah! If you put the tape rule in your
other hand, the displayed values would be upside down! I see..."]