Migration guide

RTL‑to‑HLS migration

How to decide whether a legacy Verilog or VHDL block should move to C++ HLS—and how to preserve its contract when it does.

RTL-to-HLS migration is the re-architecture of an existing Verilog or VHDL block as synthesisable C++ or SystemC, followed by generation and verification of replacement RTL. It makes sense when the block is algorithmic, requirements keep changing, or reuse across targets matters. It is not a mechanical translation: the migration succeeds only when behavior, interfaces, timing obligations, state, and memory semantics are made explicit and verified.

A good migration preserves the block's externally observable contract while allowing the internal microarchitecture to change. Before committing a production design, a focused HLS feasibility study can establish whether the selected boundary meets functional, performance, area, tool-flow, and verification needs.

Six connected stages of RTL-to-HLS migration: freeze the legacy RTL baseline, define the contract and boundary, create a bit-accurate C++ model, develop the HLS architecture, compare generated RTL, then integrate and sign off.
Read the diagram as steps

Preserve the contract. Prove the replacement.

  1. Legacy RTL baselineFreeze existing behavior and evidence before changing the architecture.
  2. Contract & boundaryDefine observable behavior and interfaces.
  3. Bit-accurate C++Establish independent tests and numeric rules.
  4. HLS architectureExplore pipeline, memory and power, performance and area tradeoffs.
  5. Compare generated RTLReuse shared test vectors and co-simulation.
  6. Integrate & sign offCheck wrappers, timing and the normal RTL sign-off requirements.
A migration should preserve one traceable evidence chain across the legacy baseline, executable contract, HLS implementation, generated RTL, and final integration.View full-size diagram

When migration makes sense

The strongest candidates are datapath-dominated blocks whose behavior is naturally described as an algorithm: filters, transforms, codecs, packet calculations, image pipelines, cryptographic kernels, and custom arithmetic. Migration is especially useful when a block needs new modes, must be retargeted, or would benefit from exploring several throughput, latency, and resource trade-offs from one verified source. If the team is new to the method, start with the fundamentals of High-Level Synthesis.

A weak candidate is dominated by asynchronous behavior, clock-domain crossings, technology-specific cells, fine-grained reset sequencing, or cycle-exact protocol control with little algorithmic work. That does not disqualify the whole subsystem. Keep those elements in RTL and migrate a well-bounded computational region. The purpose is maintainability and controlled design exploration, not maximizing the percentage of code written in C++.

What must be preserved

The first engineering output is a migration contract. It records what downstream logic and software can observe, including legal inputs, output values, ordering, error behavior, reset behavior, and timing obligations. Five areas deserve explicit treatment:

  • Functional behavior. Define arithmetic widths, signedness, rounding, saturation, overflow, exceptional inputs, and configuration semantics. Four-state RTL behavior such as unknown values needs an intentional two-state modeling policy rather than an accidental conversion.
  • Interfaces. Preserve signal direction, protocol, ordering, sidebands, and backpressure. For AXI-style streams this includes packet boundaries and valid/ready behavior; for memory-mapped interfaces it includes register effects and transaction ordering. HLS arguments become concrete ports or protocols, as illustrated by AMD's official interface-synthesis documentation.
  • Latency and throughput. Separate fixed latency, maximum latency, initiation interval, and sustained throughput. If a consumer depends on an exact response cycle, constrain the HLS implementation or retain an adapter that supplies that timing contract.
  • State and reset. Inventory every register, counter, history element, and configuration value. Map persistent state deliberately, define when it updates, and test synchronous or asynchronous reset, enable, flush, and restart behavior.
  • Memory semantics. Record port count, access conflicts, read-during-write behavior, banking, address ranges, initialization, and arbitration. A C++ array alone does not capture every property of an existing RAM or bus attachment.

A staged migration process

  1. Freeze a trustworthy baseline. Archive the RTL revision, constraints, tool versions, synthesis reports, interface specification, and passing regression. Capture representative workloads and corner cases before changing the implementation.
  2. Choose the boundary. Isolate a cohesive function with clear inputs and outputs. Identify wrappers, vendor IP, memories, or control logic that should remain in RTL.
  3. Write the executable contract. Build a bit-accurate C++ model and self-checking tests. When possible, compare against an independent reference rather than reproducing the RTL structure and its possible defects.
  4. Develop the HLS microarchitecture. Express concurrency, pipeline stages, resource sharing, storage, and interfaces through code structure and constraints. Evaluate reports against the target technology and integration budget.
  5. Verify at each representation. Compare the reference, legacy RTL, C++ source, and generated RTL with shared transactions and explicit scoreboarding.
  6. Integrate and close. Add any boundary wrapper, run subsystem regressions, and complete lint, CDC/RDC, synthesis, timing, power, implementation, and project-specific sign-off.

Verification and sign-off

Verification should answer two different questions: does the C++ model implement the intended function, and does the generated RTL implement that model? The first needs directed corner cases, constrained-random tests where useful, coverage, and comparison with the legacy block. The second uses C-to-RTL co-simulation and, where appropriate, formal or transaction-level equivalence. AMD's official flow describes how C/RTL co-simulation reuses C testbench transactions to check generated RTL.

Cycle-by-cycle sequential equivalence is appropriate only if both implementations share that contract. If the new architecture has a different pipeline depth but identical ordered results, compare transactions with latency-aware scoreboards. Backpressure, stalls, reset during traffic, minimum and maximum values, simultaneous memory events, and long-running state evolution all need dedicated tests. See the fuller HLS verification strategy for planning coverage and sign-off.

Risks and controls

  • Undocumented RTL behavior: discover it with characterization tests, waveform review, and owner interviews before defining the new model.
  • Over-constraining the replacement: distinguish genuine external requirements from incidental details of the old implementation.
  • Memory or protocol mismatch: specify adapters early and stress concurrency, stalls, bursts, and conflicting accesses.
  • Tool or library dependence: version source, directives, scripts, reports, and supported tool configuration; automate reproducible builds.
  • Verification circularity: avoid deriving every expected result from the same interpretation used to write the HLS source.

Expected deliverables

A production migration should leave an auditable design package: the migration contract and boundary specification; synthesisable C++ or SystemC; HLS constraints and reproducible build scripts; generated RTL; any integration wrapper; bit-accurate reference model; self-checking tests and vectors; C simulation and C/RTL co-simulation results; synthesis, timing, resource, and implementation reports; known limitations; integration documentation; and a traceable verification matrix. These are typical outputs of a scoped RTL-to-HLS migration engagement.

Decision criteria

Signals for deciding whether to migrate a block
SignalFavors migrationCalls for caution
Design characterAlgorithmic datapath with clear transactionsDominantly cycle-specific control or asynchronous logic
RequirementsFeatures, targets, or performance points will evolveFrozen block with no expected reuse or maintenance
VerificationStrong regression and observable boundariesUnknown intent and weak or unavailable tests
TechnologyPortable operators, memories, and interfacesHeavy dependence on custom cells or device primitives
Team and flowOwnership of C++, HLS constraints, and generated RTLNo plan for tools, CI, sign-off, or long-term maintenance

Primary sources and further reading

Frequently asked questions

Must the HLS-generated RTL be cycle-identical to the legacy RTL?

Usually not. The replacement must preserve the agreed externally observable contract. If fixed latency or cycle-level responses are part of that contract, they become explicit HLS constraints or are maintained by a small RTL wrapper. Requiring every internal register transition to match can prevent useful re-architecture.

Should an entire RTL block be migrated at once?

Not necessarily. A safer first boundary is often an algorithmic datapath with stable interfaces and strong regression tests. Protocol engines, clock-domain crossings, safety mechanisms, or tightly cycle-specific control can remain in RTL and connect to the HLS block through a documented wrapper.

How do you prove that behavior was preserved?

Define the contract first, run shared vectors against the legacy RTL and the C++ model, then use C-to-RTL co-simulation on the generated implementation. Add formal or transaction-level equivalence where appropriate, and complete the normal RTL sign-off checks before replacing the old block.

Can migrated HLS coexist with hand-written RTL?

Yes. Mixed HLS and RTL is a normal architecture. Existing protocol logic, vendor IP, memories, or hand-optimized kernels can remain in RTL, while HLS implements the portions that benefit from algorithm-level design and design-space exploration.

Is your legacy block a migration candidate?

We can assess the boundary, constraints, and verification evidence before you commit to a rewrite.

Discuss your project