Core Topics route · TFA-101

Firmware Architecture

Structure firmware so responsibilities, interfaces, dependencies and resource ownership remain understandable as the product evolves.

Foundation8 min reading + 25 min practiceWorked example

← Back to the learning library

Before you begin: Basic C or C++ and familiarity with sensors, tasks and interrupts. Revisit TFA-103 when concurrency terminology is unfamiliar.

Reading time and practice time are estimates. Examples use fictional products and illustrative engineering values, not approved product limits.

Learning objectives

By the end of this module, you should be able to:

  • Explain the purpose of firmware architecture beyond drawing a block diagram.
  • Separate hardware access, services, control logic and product behaviour.
  • Define ownership and contracts at component interfaces.
  • Select architecture views that support implementation, review and verification.

Lesson 1

1. Architecture is a set of decisions

An architecture identifies the important elements of the firmware, what each element is responsible for and the rules governing their interaction. It should make difficult decisions visible: which component owns a peripheral, where state is held, which code may block and how faults cross a boundary.

A diagram without these decisions is only a map. A useful architecture constrains implementation and gives reviewers a basis for asking whether dependencies, timing and failure behaviour are controlled.

Lesson 2

2. Separate policy from mechanism

A practical layered structure often separates hardware drivers, platform services, domain control and product application logic. Drivers know registers; control logic knows product behaviour. This permits pure logic to be tested without target hardware and reduces the effect of a processor or peripheral change.

  • Hardware abstraction: register access, pins, buses and peripheral-specific behaviour.
  • Platform services: time, storage, diagnostics, communications and scheduling.
  • Domain control: state, algorithms and safety-related decisions.
  • Application coordination: use cases and sequencing across components.

Lesson 3

3. Ownership prevents accidental coupling

Every peripheral, buffer, state variable and asynchronous event should have a clear owner. Other components use an explicit interface rather than reaching into another component's data. Ownership is especially important when interrupts, DMA and tasks can access the same resource.

Interfaces should state valid inputs, units, ranges, error responses, timing expectations and concurrency rules. These contracts become design information and natural verification targets.

Lesson 4

4. Use several small views

No single diagram answers every engineering question. Use a context view for external interfaces, a component view for responsibilities and dependencies, a run-time view for tasks and interrupts, and a deployment view for memory regions, processors and boot stages. Keep each view focused and link it to requirements, risks and tests.

Lesson 5

5. Define a contract that survives implementation

Consider an actuator interface called request_output(). Returning success could mean that a request was queued, that a register was written, or that the physical output was confirmed. These are different contracts. Define the acknowledgement point and expose completion or failure separately when action is asynchronous.

A useful contract names the owner, permitted callers, input range and units, maximum execution time, blocking rules, output validity and failure response. State who owns a buffer after submission and when it may be reused. A pointer without a lifetime agreement is a concurrency defect waiting to happen.

Interface elementIllustrative contract
RequestTarget in °C; permitted only in Ready or Active state.
AcceptanceQueue entry accepted; physical response is not yet confirmed.
Data ownershipService copies the request before returning.
CompletionEvent contains request ID, result and monotonic timestamp.
FailureQueue-full rejects the whole request without changing target.

Lesson 6

6. Account for resources and separation

Logical separation does not automatically provide fault containment. Two well-named components can still corrupt each other when they share unrestricted memory. Identify which boundaries are conventions enforced by review and which are enforced by an MPU, process isolation, peripheral ownership or independent hardware.

Budget stacks, heap or pools, queues, DMA buffers and retained storage. Stack high-water measurements are useful observations but may miss rare call paths, nested interrupts or library recursion. Combine measurement with design restrictions and analysis. Consider peak simultaneous use: an update may require extra RAM while communications and diagnostics remain active.

For each dependency, ask whether failure can propagate backwards. A logger that blocks while flash is busy can turn a diagnostic call in a safety monitor into a missed deadline. Architectural rules should therefore include latency and failure coupling as well as dependency direction.

Lesson 7

7. Review architecture through change scenarios

Review a candidate design by walking real scenarios through it: replace a sensor, add a second channel, lose the host, fill storage, or restart one service. Name each affected interface and state owner. If a small change touches most components, the design may have distributed policy or duplicated ownership.

Record an architectural decision with its context, constraints, alternatives, choice and consequences. For a fixed-size queue, explain both the memory saving and the overflow policy. A later reviewer needs to know which assumption would make the decision wrong. Update the decision when measured load or a product change violates that assumption.

Decision guide

Where should a new sensor plausibility check live?

OptionUse whenMain risk
DriverThe check concerns electrical validity or peripheral status.Driver becomes product-specific.
Domain componentThe check compares values with product or physical expectations.Needs a clean data and error interface.
Application coordinatorThe decision depends on the current use-case state.Central coordinator becomes overloaded.

Worked example

Worked example — temperature-controlled actuator

Problem

A device reads a thermistor, controls a heater and must stop heating if measurements become implausible.

Engineering approach

The ADC driver returns raw samples and hardware errors. A temperature service converts samples to °C and reports validity. A control component owns the set-point algorithm. The application state machine decides whether heating is permitted. A safety monitor independently enforces an upper limit and commands the actuator interface off.

Result

Each decision has one owner, the control algorithm can be unit tested, and the independent shutdown path can be verified by fault injection.

Engineering evidence

What should the work produce?

Firmware architecture description

Defines components, responsibilities and design rules.

Interface specification

Records contracts, units, ranges, errors and timing.

Run-time view

Shows tasks, interrupts, queues and resource ownership.

Architecture review record

Demonstrates that requirements and risks were considered.

Common failure patterns

What goes wrong?

  • A “layered” diagram while components still access global state directly.
  • One manager component that owns every decision and becomes impossible to test.
  • Hardware details leaking into product logic.
  • Architecture updated after implementation but not used to guide it.

Applied exercise · approximately 25 minutes

Design a two-channel measurement service

A controller acquires two sensors every 5 ms, publishes a combined result every 20 ms and accepts host commands. A sensor driver may take 8 ms to recover from a bus error. Produce a one-page architectural contract; these timings are exercise assumptions.

  1. Allocate acquisition, recovery, combination, command validation and diagnostics to named owners.
  2. Specify the result structure, timestamp and validity rules when one sensor is unavailable.
  3. Identify which operations must not block acquisition and give two verification scenarios.
Review a suggested approach

Let the acquisition context capture bounded samples and let a separate driver-recovery operation handle the 8 ms path. The combination service owns pairing and rejects samples outside an agreed age or synchronisation window. A result should carry channel validity, measurement time and sequence rather than a numeric value alone.

Do not hide an unavailable channel by retaining its previous reading indefinitely. Define whether a partial result is allowed by the system requirement. Test both normal pairing and a bus fault during a command burst, measuring the acquisition gap and checking result validity. The architecture is incomplete until the allowed gap and stale-data policy are agreed.

Completion criteria

  • Every mutable resource has one owner and a defined caller contract.
  • Recovery cannot silently extend the acquisition deadline.
  • Two tests observe timing and data validity, not merely successful function returns.

This is a self-directed engineering exercise, not an accredited examination. Compare the rationale as well as the final answer.

Practical check

Questions for your next design review

  • Can every peripheral and mutable state item be assigned to one owner?
  • Are dependency directions intentional and free from cycles?
  • Can important control logic execute in a host-based test?
  • Are blocking and interrupt-callable interfaces identified?
  • Can reviewers trace risk controls to architectural elements?
Key takeaway

A good firmware architecture makes change local, behaviour explainable and verification possible.

References and further reading

Educational guidance only. Apply the authorised standards editions, regulations and approved procedures relevant to your product and market. Linked publications support further study; the fictional exercises do not demonstrate compliance.