25. Glossary
- Architecture
Software Architecture defines fundamental organization of a system: how components of a software system are assembled, their relationship, and communication between them. It serves as a blueprint for the software developer team.
Among the most important elements of Architecture is a high-level data-flow plan for a system. Such a plan answers questions like:
Where do various bodies of data get stored? Examples: internal RAM, program space, external RAM, EEPROM, removable storage, internal non-volatile storage, a remote network server, etc..
From where to where does data flow within the system? Here, architectural efforts take care to ensure that bottlenecks in that data flow stay within certain acceptable ranges, and thus do not adversely affect the system.
What are the system’s critical:
time constraints,
response-time constraints (e.g. to external input), and
throughput constraints?
Architecture is a vital part of well-designed software that should be fleshed out early in a project, usually before much production code is written. Reason: architectural decisions impact the fundamental design of a system and thus its code. (Tests can be written early in the course of a project, of course, to prove throughput rates for example, which can modify architectural decisions.)
- Atomic Data
A datum (i.e. contents of a variable or data structure) is atomic if any thread observing it will always see it in a consistent state, as if operations on it have either not yet started, or have been successfully completed, and not in a state that is partially changed or otherwise inconsistent.
When reading or writing a value is started and completed with 1 CPU instruction, it is automatically atomic, since it can never been seen in an inconsistent (partially-changed) state, even from a CPU interrupt or exception. With such values, no special protection is required by programmers to ensure all threads see it in a consistent state.
- Atomic Operation
If operation X is atomic, that means that any thread observing the operation will see it either as not yet started, or as completed, and not in any state that is partially completed.
If other threads can see the operation in a partially performed state, or interfere with it, then operation X is not atomic.
If an atomic operation can fail, its implementation must return the the resource back to the state before the operation was started. To other threads it must appear as though the operation had not yet started.
- class
- subsystem
A subsystem is a unit of functionality, most often surrounding a data type or small group of data types that work closely together. In O-O languages, a class is the smallest kind of subsystem. In C, it can be less obvious that a group of functions plus one (or a small number of) data type(s) make up a subsystem, but it can be clearer if you think about it as a “sphere of responsibility” for a body of code, and clearer still if that unit of functionality is given its own C module named after the subsystem.
Source-code Reusability, Maintainability, Reliability, and Understandability all benefit from organizing a system into a series of simple, independently test-able subsystems.
- cluster
A set of modules in a directory named after the subsystem it contains.
- context
- noun,
1: the parts of a discourse that surround a word or passage and can throw light on its meaning;
2: the interrelated conditions in which something exists or occurs; environment, setting
See this short article about context for deeper understanding.
- declarations
- definitions
“Definitions” and “declarations” are very different in C. Definitions of variables define storage space, whereas declarations of variables only provide enough information for client code to access them. Definitions of functions are translated into CPU instructions, whereas declarations of functions (a.k.a. function prototypes) only provide enough information for client code to call them.
- Design by Contract
Design-by-Contract (DbC) is a design-time and coding-time discipline which leverages the power of the CPU, debugger, and other resources to help increase source code quality and reliability. It does this by clarifying the explicit agreements (contracts) between coders (writers) and clients (callers) about what is expected from the caller for the called routine to perform as expected, and what is expected of the called routine to correctly perform the task requested. It has been a cutting-edge tool in the Software Development industry since 1998, to help produce higher quality software in a shorter amount of time. Correct use of it is, of course, required in order to see the gains possible. See Appendix A — Design by Contract Details for more information.
- GPIO
General Purpose I/O – a name given to pins on many different microcontroller brands where the pin’s state is controlled directly by the firmware.
- ISR
Interrupt Service Routine
- module
In C, typically a .C and its .H counterpart (both sharing the same file-name stem) that contain all definitions, storage and operations related to a particular subsystem.
The .H file contains:
macro definitions meant for use by client code;
exported type definitions (typedefs of structs, unions, enumerations);
storage (extern) declarations for exported variables (globally-shared RAM, when important to be shared, rather than encapsulated for sake of system speed).
exported function prototypes;
in-line function definitionis (when in-line definitions are provided for client code).
All of these are part of the subsystem’s public API.
.H files never contain storage definitions (which allocate storage in RAM) or function definitions, both of which must exist exclusively in the .C file.
The .C file contains:
that subsystem’s documentation;
private macro and type definitions (not part of the subsystem’s API);
storage definitions; and
function definitions.
Anything in the .H file is never repeated in the .C file or anywhere else in the system. Instead, the .H file is
#include
-ed by the other parts of the system that wish to use the facilities of the subsystem contained by that module. This constraint is key to reducing bugs by ensuring such definitions and their mating declarations are only ever contained in one place. The .C file always includes the module’s .H file as its first line of code. This is important to validate prototypes in .H file (because the compiler will complain if they are ever different from the function definitions), provide relevant data type definitions, etc..Some developers like to have the module documentation in the .H file. However, I feel strongly that this is a bad practice for the following 3 good reasons:
When function documentation is with the function definition it subly serves as validation of correctness for that source code. Does it do what the documentation says it does? If so, there is a high probability that the source code is correct. When it does not, then the reader immediately has an important asset: he knows at least one of them is wrong, and can thereby and therewith take action to remedy. This often is a way bugs are discovered and fixed.
It is often a great way to get software right the first time by having documentation drive development. In fact, Documentation-Driven Development is a very popular and successful idea in software development.[1] And when the documentation is in the .C file, writing code against it (with it right there on the developer’s screen), especially for things like complex algorithms, is a common way, according to the testimony of many developers, a great way to get complex code right the first time.
When the function prototypes are listed together in the .H file in a form that is very readable (e.g. if all function names begin at column 16), it can be a great “quick reference” for programmers who want to get a feel for how to use the API quickly, or need to do a quick review of it while they are coding. See example.
- SoC
System on a Chip – a class of microcontroller chip. Pronounced “S-O-C.” A group of processing units on a single chip, which previously were independent chips that had to be connected together. The microcontroller chip is miracle enough, but an SoC is why complex computers can be held in our hands (see images below). Also, the more miniaturization, the more reduction in power.
- thread
In the context of this document, a thread is any sequence of CPU instructions. In “bare-metal” implementations (i.e. no OS), threads include:
the main thread executing a while(1) loop that runs the system, and
interrupts.
When running under an OS, threads include:
each task (or process),
interrupts, and
advanced OSes can have multiple “execution threads” within a processes.
- understandability
Understandability is a quality of source code and its documentation. It is the ease with which the reader can fully understanding what the CPU (and any peripherals involved) will be doing when executing a block of code, plus its impacts on the rest of the system. Understandability is inversely proportional to the time it takes for a programmer to reach full understanding of the code he is reading.
Because understandability results in more, finished, higher-quality code, with fewer bugs, in less time, it can be safely said that understandability is almost the entire reason of having a Coding Standard at all. Almost every part of this Coding Standard is aimed at shortening the time it takes for a programmer to fully — I repeat: FULLY — understand the code he is looking at. The source of a variable’s content (via scope prefix), the number of bits and signed-ness of variables (via type prefixes), what it is (via precise naming), the nature of what a function returns (via type prefixes and precise naming), diagrams, peudocode illustrating a complex algorithm — all have as their primary purpose — increasing understandability.
Footnotes