Firmware Architecture. Bare-Metal. RTOS. MCU. ARM Cortex-M. STM32. Drivers. Interrupts. DMA. State Machines. Diagnostics. Watchdogs. Communications. Low Power. Secure Boot. Firmware Update. Testing. Production Programming. Lifecycle Maintenance.
Firmware sits directly between: Physical Hardware and Product Behavior.
It reads: Sensors
controls: Actuators
manages: Power
communicates through: Interfaces
coordinates: Timers
responds to: Interrupts
stores: Configuration
detects: Faults
and decides: What the Product Should Do Next.
This is why embedded firmware cannot be treated like ordinary application software.
A desktop application can sometimes pause for hundreds of milliseconds.
A motor-control current loop cannot.
A web application can restart a process.
A battery-management system may need to preserve critical state before power disappears.
A mobile app can display an error.
An industrial controller may need to: Enter a Defined Safe State.
365PCB Embedded Firmware Development therefore approaches firmware as: Hardware-Aware Real-Time Systems Engineering.
Before writing firmware, define:
What must the product do?
What events can occur?
What timing matters?
What happens when communication disappears?
What happens when a sensor fails?
What happens during startup?
What happens during shutdown?
What happens during firmware update?
What happens after an unexpected reset?
Firmware Begins With Product Behavior — Not Source Code.
Firmware requirements should convert product requirements into measurable software behavior.
Examples:
Sensor shall be sampled at required interval.
Motor command shall be updated within required deadline.
Communication timeout shall trigger defined behavior.
Configuration shall survive power cycle.
Invalid firmware image shall not execute.
If Firmware Behavior Matters to the Product, It Should Be Defined as a Requirement.
Functional
What must firmware do?
Non-Functional
How must it behave?
Examples:
latency
determinism
boot time
memory usage
power consumption
reliability
security
maintainability.
Firmware Quality Lives Heavily in Non-Functional Requirements.
"Real-time" does not simply mean: fast.
It means:
Correct Result + Correct Time
A result arriving after its deadline may be functionally wrong even if the calculation itself is correct.
Different functions have different timing consequences.
A UI update can often tolerate delay.
A current-control interrupt may not.
Assign Timing Criticality by Physical Consequence.
A strong architecture separates responsibilities.
A typical embedded hierarchy might be: Application → Services → Middleware → Device Drivers → HAL / BSP → Hardware
The exact architecture depends on product complexity.
Layers Should Reduce Coupling — Not Merely Create More Folders.
Avoid one giant function that:
reads sensors
runs control
handles CAN
writes Flash
updates LEDs
all together.
Functions That Change for Different Reasons Should Be Separated.
Hardware-specific details should be isolated where practical.
For example:
Application logic should ideally request: SetMotorSpeed()
rather than directly manipulating: timer register X, bit Y.
Hardware Abstraction Preserves Product Logic From Device Detail.
Too much abstraction can create:
latency
memory overhead
debugging complexity.
Good Embedded Architecture Abstracts What Changes — Not What Timing Cannot Afford.
A platform layer can encapsulate:
Clock
GPIO
Timers
ADC
DMA
Communication Peripherals
Flash
Watchdog
This creates a clearer boundary between: MCU Platform
and: Product Logic.
For Cortex-M platforms, Arm CMSIS provides standardized interfaces spanning Core support, RTOS APIs, drivers, DSP, neural-network kernels, debugging and software-package infrastructure. Arm states CMSIS-CORE is implemented across more than 5,000 Cortex-M devices.
Standard Interfaces Can Reduce Platform Lock-In.
But application architecture should still remain product-driven.
A simple embedded system may not need an RTOS.
Bare-metal architecture can provide:
low overhead
direct control
simple scheduling.
Typical structure: Initialization → Main Loop → Event Handling
Simpler Architecture Can Be Better When the Product Is Simple Enough.
A traditional loop: Read Inputs → Process → Update Outputs → Communicate → Repeat
can work well when timing requirements remain manageable.
But as complexity grows:
Execution Time Becomes Shared Timing.
One slow task can delay everything else.
Instead of continuously checking everything: Events Trigger Work.
Examples:
ADC Complete
CAN Message
Button Event
Timer Expired
Sensor Ready
This can improve:
efficiency
modularity
responsiveness.
Tasks voluntarily yield control.
Advantages:
simplicity
low overhead.
But: One Misbehaving Task Can Delay the Entire System.
When firmware contains many concurrent activities, an RTOS can provide structured:
task scheduling
synchronization
timing
resource management.
Current FreeRTOS and Zephyr ecosystems illustrate how embedded RTOS platforms now extend far beyond basic task scheduling into networking, security, device management and system services.
A task should generally represent a coherent responsibility.
Examples:
Sensor Acquisition
Communication
Control
Diagnostics
Storage
Task Count Is Not a Quality Metric.
The right decomposition matters more.
Priority should follow: Deadline and Consequence.
Not: Which developer thinks their task is most important?
Critical control functions may need higher priority than:
logging
UI
background maintenance.
Interrupts allow hardware events to receive rapid attention.
Possible sources include:
Timer
ADC
DMA
UART
CAN
Ethernet
GPIO
Interrupts Connect Hardware Time to Firmware Time.
An Interrupt Service Routine should generally perform only the work that truly requires interrupt context.
A common architecture: Interrupt → Capture Critical Data → Signal Deferred Processing → Exit
Fast ISR. Structured Work Outside.
The time from hardware event to firmware response depends on:
interrupt priority
disabled-interrupt windows
higher-priority ISRs
architecture.
Worst-Case Interrupt Latency Matters More Than Average Latency.
If an event is expected every: 100 μs
but sometimes serviced at: 102 μs
and sometimes: 135 μs, that timing variation may matter.
Real-Time Quality Includes Timing Consistency.
Incorrect interrupt-priority architecture can create subtle failures.
Examples:
timing-critical interrupt blocked
RTOS API called from invalid priority context.
Priority Is Part of Firmware Architecture — Not an Afterthought.
Higher-priority interrupts may preempt lower-priority ones.
This improves latency for critical functions but makes timing analysis more complex.
Interrupt Hierarchy Creates a Real-Time Dependency Graph.
Direct Memory Access allows peripherals to transfer data with limited CPU intervention.
Useful for:
ADC
SPI
UART
I²S
memory transfers
and other streams.
DMA Moves Data Without Spending CPU Cycles on Every Byte.
DMA is not simply a performance trick.
It can enable: ADC → Buffer → Signal Processing
while CPU performs other work.
DMA Separates Data Movement From Data Processing.
A common streaming architecture: DMA fills Buffer A
while: CPU processes Buffer B.
Then swap.
Acquire and Process Concurrently.
Continuous data streams can use ring buffers.
Applications include:
UART
ADC
sensor streams
logging.
Buffer Architecture Absorbs Timing Differences Between Producers and Consumers.
If producer rate exceeds consumer rate long enough: Data Must Go Somewhere.
Options include:
drop
overwrite
throttle
signal fault.
Overflow Policy Is Product Behavior.
A robust communication system knows what to do when downstream processing cannot keep up.
Unlimited Data Production Meets Finite Memory Eventually.
Many embedded products behave naturally as states.
Example: POWER_OFF → INITIALIZING → READY → ACTIVE → FAULT → RECOVERY
Product Behavior Should Be Explicit.
Without explicit states, behavior often becomes: if
inside: if
inside: if
with global flags everywhere.
Eventually: No One Knows Which Combinations Are Legal.
A useful model: Current State + Event = Next State + Action
This makes behavior easier to:
review
test
verify.
Complex products can contain: System State
with nested: Subsystem States.
For example: SYSTEM_ACTIVE
contains:
motor state
communication state
sensor state.
Hierarchy Can Organize Complex Behavior Without Creating One Giant Flat State Machine.
The architecture should define which transitions are valid.
For example: OFF → MAXIMUM_POWER
may be illegal.
Prevent Impossible Product Behavior by Design.
Power-on behavior should be intentionally sequenced.
Possible stages: Reset → CPU Startup → Clock Initialization → Memory Initialization → Critical Hardware Check → Driver Initialization → Configuration Load → Self-Test → Application Start
Boot Is a Product State Transition.
Before main() executes, platform startup may configure:
stack
memory sections
vector table
runtime initialization.
Firmware Begins Before main().
CPU/peripheral clocks affect:
execution timing
communication baud
timers
power consumption.
Clock Configuration Is Firmware Timing Architecture.
Some systems need defined behavior if:
external crystal fails
PLL fails to lock
or timing becomes invalid.
Time Source Is a System Dependency.
After startup, firmware can often determine whether the previous reset came from:
Power-On
Watchdog
Brownout
Software Reset
Fault
depending on MCU.
The Reason the Product Restarted Is Diagnostic Information.
Recording reset cause helps answer:"Why did the device randomly restart?"
A Reset Without Context Is Lost Failure Evidence.
Subsystem initialization often has dependencies.
Example: Power Valid → Clock → Communication PHY → Protocol Stack.
Initialize According to Dependency — Not File Order.
If one peripheral fails initialization:
should the product:
retry?
continue degraded?
enter fault state?
Initialization Errors Need Product Policy.
Not every failure must make the entire product unusable.
Example: Optional Sensor Fails → Core product still operates with reduced functionality.
Robust Products Know Which Functions Are Essential.
MCU firmware lives inside finite:
Flash
and:
RAM.
Those resources must be deliberately allocated.
Embedded Memory Is Architecture — Not Unlimited Storage.
Flash may contain:
Bootloader
Application
Configuration
Calibration
Update Image
depending on design.
Memory Map Should Reflect Product Lifecycle.
RAM may contain:
stacks
heaps
DMA buffers
control-state data
communication buffers.
Know Where Runtime Memory Goes.
A linker script defines where code/data lives physically.
It may allocate:
Boot Region
Application Region
Nonvolatile Parameters
RAM Banks
The Linker Script Is Part of Embedded System Architecture.
Each task/function uses stack space.
A stack overflow can corrupt unrelated memory.
Stack Margin Is Reliability Margin.
Do not choose task stack based purely on:
"2 KB seems enough."
Use:
static analysis
high-water measurement
stress testing
where appropriate.
Measure Real Stack Use.
Many MCU/RTOS architectures can support stack monitoring.
Detect Memory Failure Before It Becomes Random Behavior.
Dynamic memory can provide flexibility.
But embedded systems may need to control:
fragmentation
allocation failure
nondeterministic timing.
Dynamic Allocation Has Lifecycle Consequences.
Safety-, control- or high-reliability systems often prefer more predictable memory ownership where appropriate.
If Memory Demand Is Known at Design Time, Runtime Allocation May Be Unnecessary Complexity.
Long-lived systems allocating/freeing variable blocks can develop fragmented memory.
Free Memory Does Not Mean Usable Contiguous Memory.
If memory allocation fails: What Happens?
A professional system does not simply assume: It never will.
An MPU can restrict which memory/peripherals different software components can access.
FreeRTOS currently provides official MPU support on multiple Cortex-M architectures, allowing privileged/unprivileged tasks and protected memory regions.
A Software Bug Should Not Automatically Have Permission to Corrupt the Entire System.
Separate: Trusted / Privileged Services
from:
Less-Trusted Application Tasks
where architecture justifies it.
Least Privilege Applies to Firmware Too.
Memory-protection mechanisms can prevent selected RAM regions from being executed as code.
Data Should Not Automatically Become Instructions.
On suitable Arm devices, hardware security partitioning can separate: Secure and Non-Secure worlds.
Potential secure functions include:
keys
device identity
cryptography
secure storage.
Arm positions Root of Trust, secure boot, identity, attestation and cryptography as foundational platform-security capabilities.
Firmware often needs to store:
user settings
calibration
network configuration
production data.
Persistent Data Is Part of Product Behavior.
Firmware evolves.
A configuration structure from: Firmware v1
may not match: Firmware v5.
Persistent Data Needs Schema Versioning.
When firmware updates, old configuration may need to be converted.
Software Upgrade Should Not Turn Valid Historical Data Into Corruption.
CRC can detect accidental data corruption.
But: CRC Is Not Cryptographic Authentication.
Use the right integrity mechanism for the threat model.
Factory calibration may contain:
offset
gain
sensor coefficients
motor parameters
RF calibration
depending on product.
Calibration Is Part of the Manufactured Product Configuration.
Calibration data should be associated with the physical unit or hardware configuration it describes.
The Right Calibration on the Wrong Hardware Is Wrong Calibration.
Products need a known configuration baseline.
Resetting to Factory Defaults Should Restore a Defined State — Not Random Compile-Time Values.
Flash/EEPROM-like memories have finite program/erase characteristics.
Do not write a counter:
every millisecond
without considering storage lifetime.
Persistent Storage Has Wear.
Strategies may include:
batching
journaling
rotating locations
depending on technology.
Storage Architecture Must Match Write Frequency.
What happens if power disappears halfway through configuration update?
Atomic Persistence Is a Firmware Reliability Problem.
One robust concept: write new → validate → commit
rather than: overwrite only copy directly.
Never Destroy the Last Known-Good State Before the New State Is Proven.
Embedded firmware often integrates:
UART
SPI
I²C
CAN / CAN FD
USB
Ethernet
wireless stacks
depending on product.
Protocol Handling Is Product Infrastructure.
A UART driver moves bytes.
A protocol gives those bytes meaning.
Physical Transport and Application Protocol Are Different Layers.
A communication protocol should define:
message boundaries
length
type
integrity.
A Stream of Bytes Needs Structure.
Depending on link and consequence, messages can use:
CRC
counters
authentication
as appropriate.
Detect Corrupted or Invalid Communication Before Acting on It.
If the system expects a command every: 100 ms
what happens after: 500 ms silence? Silence Is Also a Communication State.
Retries can improve resilience.
But unlimited retry can create:
congestion
blocking
energy waste.
Recovery Policy Needs Bounds.
A retransmitted command may arrive twice.
For certain operations:
Duplicate Execution Can Be Worse Than Packet Loss.
Protocol design should understand idempotency.
Counters can help detect:
lost
duplicate
reordered messages
where architecture requires it.
Message Order Can Be Part of Product State.
External messages are untrusted inputs.
The parser should handle:
invalid lengths
unexpected values
malformed sequences
without corrupting memory or product state.
Every External Packet Is an Input Validation Problem.
Networking interfaces often have states such as: DISCONNECTED → CONNECTING → CONNECTED → DEGRADED → RECOVERY
Communication Should Not Be Reduced to "connected = true/false."
Drivers translate software intent into peripheral behavior.
They manage details such as:
registers
interrupts
DMA
errors.
A Driver Is the Contract Between Software and Hardware.
Peripheral APIs should not simply return: success forever.
Failures can include:
timeout
bus error
invalid state
CRC error.
Driver Errors Need Meaningful Failure Semantics.
MCUs/peripherals sometimes have documented silicon errata.
Firmware may require:
workaround
restriction
sequence changes.
Firmware Must Implement the Silicon That Exists — Not the Silicon the Datasheet Wishfully Describes.
Firmware may coordinate: Sensor Power → Settling → Trigger → Acquire → Filter → Validate → Use
Reading a Sensor Register Is Not the Same as Engineering a Measurement.
Sensor fusion and control often require knowing: When the Measurement Was True.
not only: what value arrived.
Multiple sensors may need:
common trigger
synchronized timers
hardware timestamps.
Time Alignment Can Be Part of Measurement Accuracy.
Firmware can implement:
averaging
FIR
IIR
median filtering
depending on signal.
But: Filtering Adds Delay.
Signal quality and control latency must be traded together.
Small MCUs may use fixed-point arithmetic for efficient:
control
filtering
DSP.
Numerical Range Must Be Engineered Explicitly.
Modern Cortex-M and similar platforms often provide hardware floating-point capability depending on device.
This can simplify numerical algorithms.
But: Floating Point Does Not Automatically Prevent Numerical Error.
Integer overflow can silently produce incorrect control values.
Numerical Limits Are Physical Limits in Embedded Systems.
Control systems may intentionally clamp values to physical ranges.
Examples:
PWM 0–100%
Current Limit
Sensor Range
Every Number Representing Physics Should Have a Valid Physical Domain.
Confusing: milliseconds and microseconds or degrees and radians.
can create catastrophic logic errors.
Units Should Be Part of the Software Contract.
Binary protocols and stored data should explicitly define byte order.
A 32-Bit Number Has More Than One Byte Arrangement.
Some architectures impose alignment requirements.
Packed network/storage structures should be handled carefully.
Memory Layout Is Processor Architecture.
Embedded firmware should not rely accidentally on:
undefined behavior
implementation-specific assumptions.
The current C language specification is ISO/IEC 9899:2024, which explicitly includes portability considerations among the issues C implementations must manage.
"It Worked With This Compiler Optimization" Is Not a Robust Software Requirement.
volatile has specific purposes in embedded programming, particularly around hardware registers and externally changing values.
But: Volatile Is Not a Thread-Synchronization Primitive.
When multiple execution contexts access shared state:
tasks
interrupts
DMA
race conditions can appear.
Concurrency Bugs Depend on Timing — Which Makes Them Difficult to Reproduce.
Two correct pieces of code can produce an incorrect product if their relative timing changes.
Correct Functions Do Not Guarantee Correct Concurrency.
But overly long critical sections can increase:
interrupt latency
deadline misses.
Protect Shared State Without Freezing the Real-Time System.
But they can create: Priority Inversion.
A low-priority task holds a resource required by a high-priority task.
A medium-priority task then delays the low-priority owner.
Scheduling Priority Alone Does Not Guarantee Priority of Progress.
RTOS mechanisms such as priority inheritance can help depending on kernel.
Task A waits for B.
Task B waits for A.
The Product Stops While Every Thread Is Technically "Running Correctly."
Lock ordering and architecture matter.
One way to reduce shared-state complexity is to exchange:
Messages
instead of exposing internal memory.
Data Ownership Makes Concurrency Easier to Reason About.
A watchdog can detect failure to make expected progress.
But simply calling:
KickWatchdog();
from one periodic timer proves very little.
A Watchdog Should Confirm System Health — Not Timer Health.
A stronger architecture may require critical subsystems to demonstrate progress before the hardware watchdog is refreshed.
Feed the Watchdog Only When the Product Is Healthy Enough to Continue.
After watchdog reset:
should firmware:
restart normally?
log the fault?
enter degraded mode?
limit repeated restarts?
Reset Is the Beginning of Recovery — Not the End of Diagnosis.
Suppose firmware crashes five seconds after every boot.
A product can enter:
Endless Reset Loop.
Recovery architecture should recognize persistent startup failure.
MCU faults may include:
illegal access
bus faults
memory faults
usage faults
depending on processor.
A Processor Exception Is Valuable Diagnostic Evidence.
When possible, preserve useful information such as:
Program Counter
Stack Pointer
Fault Status
Reset Cause
Firmware Version
A Crash Without Context Is a Mystery That Firmware Created for Itself.
Assertions can catch violated assumptions during development.
Example:
buffer length must never exceed maximum.
Detect Broken Assumptions Close to Where They Break.
A development build may halt aggressively.
A production product may need:
controlled recovery
logging
instead.
Debug Behavior and Field Behavior Can Differ.
Strong firmware continuously evaluates:
"Does what I am observing make physical sense?"
Examples:
Motor command high, but current zero.
Temperature changes unrealistically fast.
Sensor value outside physical range.
Diagnostics Connect Software Logic to Physical Reality.
A value can be electrically valid but physically impossible.
Data Validity ≠ Physical Plausibility.
Two independent signals can sometimes verify each other.
Example: Motor speed vs. encoder change.
Redundant Physics Can Reveal Sensor Failure.
Different faults can be:
Warning
Recoverable
Degraded
Critical
Latched.
Fault Severity Should Follow Product Consequence.
A low-level driver error should not automatically become:
random application behavior.
The software architecture should define how errors propagate upward.
Fault Information Needs a Route Through the Software Stack.
One failed subsystem should not unnecessarily destabilize everything.
Software Architecture Can Create Fault Boundaries Just Like Hardware Architecture.
Embedded logs can contain:
event
timestamp
state
error code
system metrics.
Logs Should Explain Product Behavior — Not Fill Flash With Debug Text.
Examples:
ERROR
WARNING
INFO
DEBUG
Allow different detail levels across:
development
production
field service.
Machine-readable event records are often more valuable than:
"Something went wrong."
Diagnostics Should Be Data.
Limited embedded memory can use a circular log preserving recent history.
Keep the Events Leading Up to the Failure.
When a critical fault occurs, save a snapshot:
Voltage
Current
Temperature
State
Communication
Firmware Version
Failure Context Accelerates Root-Cause Analysis.
System timing should distinguish: elapsed time
from: calendar time.
Control Time Should Not Jump Because the Wall Clock Was Corrected.
A 32-bit millisecond timer eventually wraps.
Firmware that assumes:
time always increases numerically
can fail after weeks.
Long-Life Products Must Survive Counter Wraparound.
Timeout logic should remain correct across timer rollover.
"It Ran for 49 Days and Failed" Is a Classic Firmware Architecture Problem.
Battery-powered products need coordination of:
CPU Sleep
Peripheral State
Sensors
Radio
Memory
Wake Sources
Low Power Is a System State — Not One Sleep Instruction.
Example: ACTIVE → IDLE → SLEEP → DEEP SLEEP
Firmware should know:
what remains powered
what state is lost
how wake occurs.
Possible wake events:
timer
GPIO
communication
sensor interrupt.
Wake Architecture Determines Product Responsiveness.
Deep power savings often increase wake time.
Energy and Responsiveness Trade Against Each Other.
Unused peripherals can be:
clock gated
powered down
where hardware permits.
Firmware Can Reduce Power Without Changing Hardware.
Some products adjust clock frequency according to workload.
This influences:
timing
power
peripheral clocks.
Performance Management Changes Firmware Timing Assumptions.
Measure energy per product activity:
Sensor Read
Radio Transmission
Processing
Sleep.
Optimize Energy by Behavior — Not Only Average Current.
The bootloader determines which firmware executes.
It can manage:
image validation
selection
update
recovery.
The Bootloader Is the Gatekeeper of Product Software.
MCUboot is one current open secure-boot implementation for 32-bit MCUs and provides common infrastructure for image layout and software upgrades across multiple embedded platforms.
A secure-boot architecture verifies that firmware is authorized before executing it.
The Processor Should Know Who Is Allowed to Give It Instructions.
Code signing can establish that an image came from an authorized source.
Firmware Version Is Not Proof of Firmware Authenticity.
The system should detect unauthorized modification.
This directly reflects NIST's Firmware Resiliency model: Protection → Detection → Recovery.
Security is incomplete if the device can detect corruption but cannot recover.
A Secure Brick Is Still a Broken Product.
Products increasingly need software changes after manufacturing.
Updates can provide:
bug fixes
features
security fixes.
Update Architecture Is Lifecycle Architecture.
A robust updater must consider: What happens if power disappears halfway through? Firmware Update Must Be Transactional.
Recovery architecture can preserve a valid image while a new image is being installed.
Never Sacrifice the Last Bootable Firmware Until the New Firmware Is Trusted.
If new firmware fails validation or runtime health checks: Return to Known Good.
Security-sensitive products may also need to prevent installation of known-vulnerable old firmware.
Reliability Wants Rollback.
Security May Need Controlled Rollback.
The architecture must reconcile both.
Firmware intended for: Board Rev C
may be wrong for: Board Rev A.
Firmware Must Know Which Hardware It Controls.
Possible mechanisms include:
EEPROM
resistor IDs
device IDs
production metadata.
Configuration Compatibility Should Be Machine-Checkable.
A manufactured electronic product is really a controlled combination of:
PCB Revision
BOM Variant
Firmware
Calibration
Configuration.
Firmware Release Is Product Release.
Manufacturing must load:
correct bootloader
correct application
correct configuration
onto the correct hardware.
Firmware Programming Is a Manufacturing Process.
Do not simply assume:
programmer reported success.
Verification can include appropriate:
hash
readback
boot checks
depending on production architecture.
Program → Verify → Identify.
Products may receive unique:
serial numbers
certificates
device keys
during manufacturing.
Manufacturing Can Create the Product's Digital Identity.
Security credentials should be handled using controlled processes appropriate to threat and product architecture.
Secrets Should Not Become Ordinary Production Data.
Development needs powerful:
JTAG
SWD
console
access.
Production security may require it to be:
restricted
authenticated
disabled
depending on threat model.
Debug Access Is Both an Engineering Tool and a Security Boundary.
C remains central to embedded firmware, and the current international C standard is ISO/IEC 9899:2024.
C++ and other languages can also be appropriate depending on platform and project.
Language Selection Should Follow Product Requirements, Toolchain, Runtime Constraints, Team Capability and Safety/Security Needs.
A serious project can establish rules around:
naming
control flow
conversions
pointer use
error handling
concurrency.
Coding Style Is Easy.
Coding Discipline Is Harder.
Embedded C can suffer from:
buffer errors
integer errors
pointer misuse
invalid memory access.
SEI's CERT C work and NIST's SSDF both reinforce secure coding and systematic vulnerability reduction as part of professional software development.
Firmware Security Begins Before Cryptography.
A high-quality firmware project should treat compiler diagnostics seriously.
Warning Noise Hides Real Defects.
The target should be: Zero Unexplained Warnings.
Static analysis can identify classes of defects without executing the program.
Potential findings include:
dead code
memory misuse
unsafe conversions
concurrency risks.
Find Bugs Before Hardware Has to Demonstrate Them.
Automated tools cannot understand every system assumption.
Human review asks:
Is this algorithm correct?
Is timing correct?
Is the fault response correct?
Static Analysis Finds Patterns.
Engineers Find Bad Reasoning.
A second engineer sees:
What the Code Actually Says.
The original author often sees:
What They Intended It to Say.
That difference matters.
Individual algorithms/modules can be tested independently.
Examples:
CRC
State Machine
Filter
Protocol Parser
Calibration Algorithm
Test Logic Before It Depends on Hardware.
Hardware-dependent modules can be structured so portions of product logic can run with simulated dependencies.
Testability Is an Architecture Property.
Some embedded logic can run on a development computer for rapid automated testing.
The MCU Should Not Be the Only Place Firmware Can Be Tested.
Once modules work individually:
Do They Work Together?
Integration failures often emerge around:
timing
state
interfaces
resource ownership.
HIL can connect real firmware/controller hardware with simulated external behavior.
Useful for testing:
sensors
communications
product states
failure responses.
Test Product Behavior Without Waiting for Every Physical Scenario.
Controlled development environments can emulate:
sensor timeout
communication loss
invalid data
memory condition
to verify designed fault handling.
Don't Wait for the Field to Discover Your Recovery Logic.
Some bugs emerge only after:
hours
days
millions of messages.
Examples:
memory leak
counter wrap
race condition
resource leak.
Embedded Reliability Has a Time Dimension.
Exercise:
Maximum Communications
Maximum Sensor Rate
High CPU Load
Memory Pressure
where appropriate.
Test the Firmware Where Scheduling Margin Is Smallest.
Measure:
task execution
ISR duration
scheduling latency.
Real-Time Claims Should Be Measured.
A system running at:
99.9% CPU
may work nominally but have almost no transient margin.
Spare Compute Capacity Is Timing Margin.
Average execution time can hide rare long paths.
Deadline Engineering Needs Worst-Case Thinking.
Instead of reporting:
average response = 20 µs,
measure the distribution.
The Rare 200 µs Event May Be the One That Breaks the Product.
Modern embedded systems can use runtime tracing to analyze:
scheduling
interrupts
events
resource use.
Arm's CMSIS ecosystem includes visibility/debug infrastructure, while FreeRTOS and other RTOS platforms support increasingly sophisticated tracing ecosystems.
Make Invisible Timing Visible.
Every software change can automatically trigger: Build → Static Analysis → Unit Tests → Integration Tests
where infrastructure permits.
Firmware Quality Should Not Depend on Someone Remembering to Run the Tests.
Given the same:
source
toolchain
configuration
the build process should produce controlled output.
A Release Should Be Reconstructable.
Compiler version can affect:
code generation
warnings
behavior.
Toolchain Is Part of Firmware Configuration.
Firmware may include:
RTOS
libraries
vendor SDK
protocol stacks.
These dependencies need:
version
licensing
security
update
management.
Third-Party Code Becomes Your Product Code When You Ship It.
Connected embedded products increasingly need visibility into software components and dependencies.
You Cannot Maintain What You Cannot Identify.
Security should be built into: Requirements → Architecture → Implementation → Testing → Release → Maintenance.
NIST's SSDF explicitly recommends integrating secure-development practices throughout the SDLC; as of August 2026, v1.1 remains final while v1.2 is still draft.
Security Is a Development Process — Not a Penetration Test at the End.
CISA's current Secure-by-Design guidance similarly pushes software manufacturers to take ownership of customer security outcomes throughout the product lifecycle, including embedded and OT software contexts.
Security Decisions Belong in Product Architecture.
Every source change should be traceable.
A release should identify:
commit
branch/tag
configuration
build environment.
"Latest firmware.zip" Is Not Configuration Management.
For critical changes, preserve:
author
reviewer
rationale.
Engineering Decisions Need History.
Bugs should connect: Observed Problem → Root Cause → Code Change → Verification
Defect Closure Requires Evidence.
For demanding products: Requirement → Software Component → Test → Result
Firmware Features Should Trace From Intent to Evidence.
Fixing one issue can break another.
Every Resolved Bug Should Become a Candidate Regression Test.
Manufacturing may need specialized firmware to:
exercise interfaces
read sensors
drive outputs
accelerate tests.
Manufacturing Firmware Is Part of DFT.
Dedicated test firmware can be useful.
But release control must ensure the customer unit leaves the factory with: Correct Production Image.
Manufacturing can run automated calibration sequences.
Example: Apply Known Stimulus → Measure → Calculate Coefficient → Store → Verify
Firmware Can Turn Manufacturing Equipment Into a Closed Calibration Loop.
Production can record:
Serial Number
PCB Revision
Firmware Version
Calibration
Test Result
Every Product Should Know What Software It Left the Factory With.
EVT asks: Does the Hardware/Firmware Architecture Work?
Focus on:
bring-up
drivers
major state machines
instrumentation
hardware learning.
EVT Firmware Should Maximize Engineering Visibility.
DVT increasingly validates:
final behavior
failure recovery
power states
communications
environmental interaction.
DVT Should Test the Product — Not the Developer's Debug Setup.
PVT focuses on:
programming
calibration
production configuration
manufacturing test
traceability.
Firmware Must Become a Repeatable Production Process.
The product lifecycle continues after shipment.
Firmware may need:
bug fixes
security updates
compatibility maintenance.
Shipping v1.0 Is the Beginning of Firmware Lifecycle.
New firmware may need to support:
old hardware
old configuration
installed accessories.
Product History Becomes Software Architecture.
Older firmware may encounter newer peripheral revisions or data formats.
Where product architecture requires it: Version Boundaries Should Be Explicit.
A replacement sensor or flash memory can require firmware adaptation.
Supply-Chain Changes Can Become Software Changes.
Toolchains, SDKs and libraries can reach end of support.
Firmware Lifecycle Includes Its Development Infrastructure.
A long-lived product might migrate from: MCU A to MCU B.
Strong architecture can preserve much of: Product Logic
while changing: Platform Layer.
Portability Is an Architecture Investment.
Multiple products can share:
Boot Architecture
Drivers
Communication
Diagnostics
Update
Security
while varying application behavior.
Good Firmware Architecture Can Become a Product Platform.
Reusable software should define:
assumptions
interfaces
dependencies
tests.
Reuse Validated Behavior — Not Copy-Pasted Source Files.
Do not judge embedded software by: lines of code.
Better metrics include:
test coverage where meaningful
timing margin
defect trend
memory margin
recovery behavior.
Less Code Can Be Better Firmware.
The strongest embedded development does not say: hardware is finished, now firmware can start.
Firmware engineers should influence:
pin mapping
interrupts
DMA
memory
debug
test access
boot architecture.
Firmware Requirements Should Influence Hardware Before PCB Release.
An MCU may technically have: three UARTs
but only one supports the desired:
DMA
pins
low-power wake
combination.
Peripheral Assignment Is Hardware/Firmware Co-Design.
Firmware use of:
timers
ADC
PWM
communication
must align with the schematic pin plan.
Pinmux Is Product Architecture.
One MCU timer may be required for:
PWM
capture
encoder
scheduler tick.
Timers Are Shared Real-Time Hardware Resources.
Likewise, DMA channels/requests may conflict.
"Peripheral Has DMA" Does Not Mean Every DMA Requirement Works Simultaneously.
External:
NOR
NAND
EEPROM
RAM
choices change firmware architecture.
Hardware Storage Architecture Becomes Software Storage Architecture.
Expose enough development access during EVT.
Otherwise a simple firmware bug can become: A Week of Guessing.
A good embedded product lets engineering observe:
state
timing
faults
critical measurements.
Observability Is an Engineering Feature.
Development products may include a controlled diagnostic command interface.
It can support:
reading state
executing tests
calibration.
Debug Interface Should Expose Engineering Information Without Becoming Production Risk.
Production devices may provide restricted diagnostics for authorized servicing.
Serviceability Should Be Designed — Not Discovered After Returns Begin.
Optimization should follow measurement.
Do not optimize: what looks slow.
Profile → Identify Bottleneck → Optimize → Measure Again.
Possible optimization targets include:
CPU
latency
memory
Flash size
energy.
"Faster" Is Not the Only Embedded Optimization.
Highly clever code can reduce:
readability
portability
testability
for negligible gain.
Optimize What the Product Needs — Not What Makes the Code Look Advanced.
A function taking: 10 µs every time
may be preferable to one taking:
5–50 µs
in a real-time loop.
Predictability Can Be More Valuable Than Peak Speed.
Errors should communicate useful meaning.
Instead of:
ERROR = 1
use structured fault semantics.
Diagnostics Need Information Density.
Knowing:
I²C timeout
is useful.
Knowing:
Sensor 3, I²C timeout, state=measurement, retry=3
is much more useful.
Context Turns Errors Into Evidence.
Connected products may report selected:
health
firmware version
errors
performance metrics.
Field Telemetry Can Turn Millions of Operating Hours Into Engineering Knowledge.
Telemetry should collect only appropriate data and protect it according to the product's security/privacy requirements.
Observability Should Not Create Unnecessary Exposure.
Suppose: 0.3% of devices reset during a specific operation.
Firmware logs can correlate:
hardware revision
firmware version
temperature
event
Field Data Can Reveal Failure Patterns a Laboratory Never Saw.
Every field firmware failure should improve:
architecture
tests
diagnostics
design rules.
Every Failure Should Make the Firmware System Smarter.
At the highest level: Product Requirements → Behavior Definition → Timing Requirements → Firmware Architecture → Hardware / Firmware Partitioning → Boot Architecture → HAL / BSP → Drivers → Interrupt / DMA Architecture → Bare-Metal / RTOS Decision → Task / State-Machine Architecture → Memory Architecture → Communication → Sensor / Control Processing → Power Management → Diagnostics → Fault Management → Watchdog → Persistent Data → Calibration → Security → Secure Boot → Firmware Update / Recovery → Coding Standards → Static Analysis → Unit Test → Integration Test → HIL → Timing Validation → EVT → DVT → Production Programming → PVT → Field Update → Field Diagnostics → Lifecycle Maintenance → Predictable Physical Product Behavior
That is the difference between: Writing Firmware
and: Engineering an Embedded Product.
Depending on project scope, a 365PCB ODM firmware program may include:
Firmware Requirements Specification
Hardware / Firmware Partitioning
Firmware Architecture Document
Software Module Architecture
Bare-Metal Architecture
RTOS Architecture Inputs
Startup Architecture
Boot-Sequence Definition
Reset Architecture
Clock Initialization
HAL Architecture
BSP Inputs
Device Driver Development
GPIO / ADC / DAC Drivers
Timer / PWM Drivers
DMA Architecture
Interrupt Architecture
Interrupt-Priority Plan
State-Machine Design
Event-Driven Architecture
Task Architecture
Real-Time Timing Requirements
Data Acquisition
Sensor Processing
Digital Filtering
Control Logic
Communication Drivers
UART / SPI / I²C
CAN / CAN FD
USB
Ethernet
Protocol Implementation
Communication Timeout / Recovery
Memory Map
Flash / RAM Allocation
Stack Analysis
Heap / Static Allocation Strategy
MPU Inputs
Privilege Separation Inputs
Persistent Data Architecture
Configuration Management
Calibration Storage
Nonvolatile Data Integrity
Power-Fail Recovery
Low-Power Firmware
Sleep / Wake Architecture
Dynamic Power Management
Watchdog Architecture
Fault Management
Diagnostic Architecture
Reset-Cause Logging
Crash Context
Event Logging
Freeze-Frame Data
Firmware Versioning
Hardware / Firmware Compatibility
Bootloader Inputs
Secure-Boot Inputs
Firmware Update Inputs
Rollback / Recovery Inputs
Device Identity Inputs
Production Provisioning Inputs
Production Programming
Programming Verification
Production Test Firmware
Calibration Firmware
Firmware Configuration Control
Coding Standard
Static Analysis
Compiler-Warning Closure
Peer Review
Unit Test
Integration Test
HIL Test Inputs
Fault-Injection Test Inputs
Timing / Latency Measurement
CPU / Memory Profiling
Long-Duration Test
Stress Test
Regression Test
CI Build Inputs
Reproducible Build Process
Release Management
EVT Firmware
DVT Firmware
PVT Firmware
Production Release
Field Update Strategy
Field Diagnostics
Firmware Lifecycle Maintenance
ECO / Revision Compatibility
Firmware Release Documentation
The actual engineering depth should follow: MCU / Processor + Timing Criticality + Product Complexity + Connectivity + Safety + Security + Power Requirements + Lifecycle + Production Volume.
Embedded firmware capability is platform- and product-specific. Architecture, real-time performance, memory usage, security, update strategy, power consumption and reliability depend on the selected MCU/processor, peripheral architecture, RTOS or bare-metal environment, hardware design, communication requirements, timing constraints, safety requirements and product lifecycle.
We Don't Judge Firmware by Whether It Compiles.
We Judge It by Whether the Product Behaves Predictably.
Code Can Be Correct and the Product Can Still Be Wrong.
Bring Us the Product Behavior — Not Just the MCU
You can begin with:
Product Requirements
Schematic
MCU / Processor
Existing Firmware
Existing Source Code
Communication Protocol
Sensor Requirements
Control Requirements
Timing Requirements
Existing Bug / Reset Problem
Power Requirements
Firmware Update Requirements
or simply: Tell Us What the Product Must Do — and What Must Happen When Something Goes Wrong.
365PCB can help translate: Physical Product Requirement → Firmware Architecture → Real-Time Behavior → Verification → Manufacturing → Lifecycle.
Don't Just Make the Code Run.
Define the Product States.
Understand the Timing.
Architect the Interrupts.
Control the Memory.
Synchronize the Data.
Validate the Inputs.
Design the Fault Behavior.
Make the Watchdog Meaningful.
Preserve Failure Evidence.
Manage the Power States.
Protect the Firmware.
Make Updates Recoverable.
Control Hardware / Firmware Compatibility.
Test the Real-Time Behavior.
Program It Correctly in Manufacturing.
Maintain It After Shipment.
365PCB Embedded Firmware Development connects:
Hardware + Real-Time Software + Control + Communications + Diagnostics + Security + Manufacturing
into one coordinated embedded-product engineering process.
Embedded Firmware Is Not Just Code Running on a Microcontroller.
It Is the Real-Time Behavior of the Physical Product.
And:
Hardware Defines What the Product Can Do.
Firmware Defines How the Product Behaves.