Migrating Legacy VHDL Code to Modern CPLDs: A Step-by-Step Tutorial

When the old board finally gave up the ghost, the first thing the shop floor asked was “Can we keep the same VHDL?” The short answer is yes – but only if you give it a little TLC. New CPLDs bring better power, more pins, and a healthier supply chain, but they also expect a cleaner, more modern VHDL style. In this post I’ll walk you through the exact steps I use at CPLD Insights when I breathe new life into legacy code.

Why Move to a New CPLD?

The world of programmable logic moves faster than a clock edge on a 500 MHz FPGA. Here are three practical reasons to upgrade:

  • Component availability – Many of the 90‑nm CPLDs we used in the early 2000s are now end‑of‑life. Finding a fresh batch can take weeks or cost a premium.
  • Power and performance – Modern devices run cooler and can toggle more logic per watt. That translates to longer battery life in portable gear.
  • Design tools – Today’s synthesis tools understand a richer subset of VHDL and can catch bugs that older compilers missed.

If any of those hit home, it’s time to start the migration.

Step 1: Take Inventory of Your Design

Before you touch a single line of code, list what you have.

  1. Source files – Gather every .vhd file, testbench, and any generated netlists. Put them in a single folder named legacy_src.
  2. Device constraints – Look for .ucf or .pcf files that map pins to the old CPLD. If they are missing, you’ll need the board’s schematic.
  3. Tool versions – Note the exact version of the old synthesis tool (often a Xilinx ISE or Altera Quartus version). This helps you understand which language features were supported.

I still remember the first time I opened a 1998 project in a modern IDE – the file tree looked like a fossil dig site. A quick inventory saved me hours of hunting for that one stray entity file that held the whole state machine.

Step 2: Clean Up the VHDL

Legacy VHDL tends to carry a few habits that modern tools flag as warnings or outright errors.

Remove Deprecated Libraries

Older code often uses ieee.std_logic_unsigned or ieee.std_logic_arith. Replace them with ieee.numeric_std. The change is simple:

-- Old
use ieee.std_logic_unsigned.all;
-- New
use ieee.numeric_std.all;

Consolidate Unused Packages

If you see use work.my_pkg.all; but the package isn’t referenced anywhere, delete the line. Unused packages slow down synthesis and can hide real warnings.

Simplify Generics and Constants

Legacy designs sometimes hard‑code widths like signal data : std_logic_vector(7 downto 0); while also having a generic DATA_W. Align them so the generic drives the width everywhere. This makes later pin‑mapping easier.

Resolve Mixed Sensitivity Lists

Older VHDL often mixes process(clk, reset, data) with combinational logic. Modern best practice is to keep clocked processes separate from purely combinational ones. Split them if needed – it reduces simulation surprises.

Step 3: Map the Pin Constraints to the New Device

Pin numbers on a new CPLD will not match the old board. Here’s how to translate them safely.

  1. Open the board schematic – Identify each signal’s function (e.g., LED_OUT, UART_TX).
  2. Create a fresh constraints file – Name it new_device.xdc for Xilinx or new_device.qsf for Intel. Use the same signal names from your VHDL.
  3. Assign pins – Example for a Xilinx CPLD:
set_property PACKAGE_PIN A1 [get_ports LED_OUT]
set_property IOSTANDARD LVCMOS33 [get_ports LED_OUT]

If you have a lot of pins, write a small script (Python or Bash) that reads the old .ucf and rewrites the lines with the new package pin names. It saves a lot of copy‑paste.

Step 4: Choose the Right Synthesis Tool

CPLD Insights recommends the following flow:

  • Xilinx – Vivado WebPACK (free) supports the newer CoolRunner‑II and XC9500 families.
  • Intel (Altera) – Quartus Prime Lite Edition works well for MAX II and MAX V devices.

Install the tool, open a new project, and point it at the cleaned VHDL files and the new constraints file. Most modern tools will automatically infer the target device from the constraints, but double‑check the part number in the project settings.

Step 5: Run Synthesis and Fix the Warnings

When you hit “Run Synthesis,” expect a handful of warnings. Treat them as clues, not errors.

  • Undriven nets – Often a leftover signal that was removed from the design but still appears in a constraint file.
  • Clock domain crossing – If your legacy code mixes asynchronous clocks, modern tools will flag potential metastability. Insert proper synchronizers or use a single clock if possible.
  • Resource over‑utilization – New CPLDs may have fewer logic cells than the old ones if you were using a high‑density part. If you hit a limit, consider refactoring large case statements into smaller ones or using shared resources.

I once spent an afternoon chasing a “Latch inferred” warning that turned out to be a missing else clause in a combinational process. Adding the else not only cleared the warning but also eliminated a subtle glitch in the hardware.

Step 6: Simulate the Updated Design

Before you program the chip, run a functional simulation.

  1. Create a testbench – If you have one from the legacy project, great. If not, write a simple stimulus that toggles inputs and checks outputs.
  2. Use the built‑in simulator – Vivado’s xsim or Quartus’s ModelSim‑Altera are fine for basic verification.
  3. Check timing – Modern CPLDs have faster internal routing, but you still need to verify that your setup and hold times meet the new device’s specifications.

A quick tip: add a report "Simulation complete" at the end of the testbench. It gives you a clear marker when the run finishes, especially when you run batch simulations.

Step 7: Program and Validate on Hardware

Now the fun part – loading the bitstream onto the new CPLD.

  1. Generate the programming file – Usually a .bit for Xilinx or .pof for Intel.
  2. Connect the programmer – Use a USB‑Blaster or a JTAG cable that matches the new device.
  3. Program the device – Follow the tool’s “Program Device” wizard.
  4. Run a hardware sanity check – Power up the board, toggle a few inputs manually, and verify the outputs with an LED or a logic analyzer.

If something looks off, go back to the simulation step. Most issues are caught early, but a stray pin assignment can still slip through.

Step 8: Document the Migration

Your future self will thank you. Write a short migration note that includes:

  • Old device part number and new device part number.
  • List of changed VHDL files and why they were altered.
  • Updated constraints file location.
  • Any known quirks (e.g., “reset is active‑low on the new CPLD”).

I keep a MIGRATION.md file in the project root. It’s become a habit that saves weeks when a colleague inherits the design.


Migrating legacy VHDL to a modern CPLD is less about rewriting everything from scratch and more about giving the old code a gentle polish. By inventorying, cleaning, re‑pinning, and verifying step by step, you can keep your design alive without reinventing the wheel. Happy coding, and may your logic be glitch‑free!

Reactions