A decision framework for allocating functions between FPGA logic and embedded software while keeping interfaces, verification and delivery risks visible.
Treat hardware and software as one system
In an FPGA-based product, the most consequential architectural decision is often not which processor or toolchain to use. It is where each function should run. A function placed in programmable logic can gain deterministic parallel execution and low latency, but it becomes more expensive to change and verify. The same function in software may be easier to update and observe but may miss timing, throughput or power constraints.
Hardware-software co-design makes that boundary an explicit engineering decision instead of a late handoff. Teams assess the complete behaviour, partition functions using shared criteria and verify the interface as part of the product. This is especially important when embedded C or C++, Python tooling and VHDL or another hardware description language must evolve together.
The client outcome is a design that meets system constraints without putting every demanding function into hardware or every changeable function into software by habit. The partition should be justified by measurements, written assumptions and a path to revise it while uncertainty remains.
Agree on a system owner who can arbitrate across disciplines. Separate backlogs and suppliers are common, but performance and reliability belong to the combined product. A shared owner keeps local optimisation from moving cost or risk across the boundary unnoticed.
Keep the boundary movable during early learning. Use replaceable interfaces, test doubles and measurement hooks so a function can move after evidence changes the trade-off. Freezing the partition before realistic workloads, device limits and update needs are known turns an early estimate into expensive rework. The architecture review should therefore approve both the current decision and the remaining validation plan.
Quantify constraints before partitioning
Words such as fast, real-time and efficient are not architecture requirements. Replace them with budgets: maximum end-to-end latency, sustained and burst throughput, jitter tolerance, available logic and memory, processor load, power envelope, startup time and permitted failure rate. Include environmental and safety constraints where they apply.
Measure the whole path rather than a favourite algorithm in isolation. Transfers across a processor-FPGA boundary consume time, bandwidth and buffers. Interrupt handling, cache behaviour, DMA setup and conversion between data representations can erase an apparent acceleration benefit. A representative workload and an agreed measurement method keep comparisons honest.
Capture uncertainty in the budgets. Typical, worst-case and degraded conditions are different design inputs. When measurement hardware or final silicon is unavailable, label estimates and give them a validation date. An unlabelled estimate easily becomes a fixed requirement or a false margin.
Separate hard constraints from preferences. A control loop deadline may be non-negotiable; a particular vendor library may simply be familiar. This distinction gives the team room to explore options without weakening the product requirement.
- Latency and jitter at the system boundary, not only inside a component.
- Throughput under representative bursts and back-pressure.
- Logic, memory, processor, thermal and power budgets.
- Update frequency, expected product lifetime and field-maintenance path.
- Safety, security, diagnostic and recovery requirements.
Score every candidate function with the same questions
Candidate functions should be compared on more than raw speed. FPGA logic is attractive for stable, parallel, bit-level or deeply pipelined work with strict timing. Software is attractive for complex control, frequent change, rich libraries and behaviour that benefits from straightforward logging and field updates. Many designs need a hybrid: hardware handles the predictable data path while software owns policy and orchestration.
Add lifecycle cost to the score. Ask how the function will be debugged on a real unit, how a defect is patched, what skills are needed in five years and how tool or device obsolescence affects maintenance. A small performance win may not justify a specialised verification and release path. Conversely, repeated processor scaling may be more costly than a carefully bounded accelerator.
Record the rejected alternative and the evidence behind the decision. This prevents the same debate from restarting and makes later revision rational when workloads or device choices change.
Design the hardware-software contract as a product interface
Registers, memory maps, message formats, interrupts and timing rules form a contract. Give the contract a version, an owner and executable checks. Specify endianness, alignment, ranges, reset values, error states, timeouts and what happens when either side restarts. Ambiguity at this boundary creates defects that are difficult to reproduce because each team sees only half of the state.
Use generated artefacts where practical so firmware headers, documentation and test models come from one definition. Provide a simulation or emulation path that lets software development continue before final hardware exists. Keep observability in the design: counters, trace points, health registers and meaningful error codes reduce laboratory time and later field-service cost.
Specify compatibility deliberately. Decide whether software can work with multiple bitstream versions, how capabilities are discovered and which combinations are supported. Reject an unsafe combination visibly at startup instead of allowing a subtle runtime mismatch.
Security belongs in the contract too. Define which inputs are untrusted, how updates are authenticated, how debug access is controlled and how malformed data is contained. A technically correct data path can still become a product risk if recovery and access rules are added only after integration.
Verify components separately and behaviour together
Unit tests for firmware and simulations for logic are necessary but not sufficient. The highest-risk failures often live in sequencing, timing and recovery between the two. Build a verification ladder: pure models, RTL simulation, software against a virtual interface, co-simulation, hardware-in-the-loop and representative target testing. Each level should answer a different question.
Trace every critical requirement to a test and preserve test inputs as versioned assets. Include overload, clock or communication disturbance, partial update, invalid data and restart scenarios. Define who triages cross-boundary failures so they do not bounce between teams. A shared defect language based on observed system behaviour is more useful than assigning blame to hardware or software prematurely.
Delivery is ready when the build is reproducible, the bitstream and software versions are linked, release evidence can be recreated and a field unit can report what it is running. These controls matter as much as initial functionality over a long product lifecycle.
Rehearse an update failure and a recovery before release. Confirm what happens after power loss, partial programming or an incompatible package. Recovery that exists only as a laboratory instruction is not yet a dependable field capability.
Partitioning checklist for the architecture review
Review this list for each meaningful boundary, not only once for the whole device. If evidence is missing, fund a focused measurement or prototype before freezing the architecture. The purpose is to retire the most expensive uncertainty while change is still affordable.
- Are system constraints quantified with representative workloads?
- Has transfer and buffering overhead been included in comparisons?
- Is the function stable enough to justify hardware implementation?
- Are interface semantics, timing, reset and error behaviour versioned?
- Can software progress before final hardware is available?
- Do co-verification tests cover overload, invalid input and recovery?
- Are build artefacts reproducible and firmware-bitstream versions linked?
- Is the maintenance path realistic for the product lifetime and available skills?