Preserving RTL provenance (component context) through Yosys synthesis and AIG mapping

Hello everyone,

Edit: I am using version 0.33, installed via apt install (Ubuntu/WSL).

I’m working on a project that uses Yosys to analyze synthesized circuits. I’m taking RTL designs (in my case, a parameterized FIR filter in SystemVerilog originally built and simulated in Vivado) and converting them to a gate-level JSON using Yosys for downstream analysis.

Here’s my challenge:
after running passes like

read_verilog fir_test.v
hierarchy -top kepq4
proc; opt; flatten
techmap -autoproc -map +/xilinx/cells_sim.v
opt; aigmap
write_json yosys_out.json

the resulting $and and $not cells in the final JSON have lost nearly all human-readable context. Importantly, I can no longer tell which RTL component or vendor primitive (e.g., LUT2, CARRY4, FDCE) each gate originally came from. The src attribute usually points to internal Yosys source files (like aigmap.cc) rather than the original Verilog locations. The parameters and attributes fields are empty.

My goal is to preserve or reconstruct the “history” of each synthesized node and ideally to be able to determine which original component the node originated from (i.e. this particular AND gate comes from a the LUT2 primitive, etc.).

So I’m wondering:

  1. Is there a recommended or supported way to preserve RTL provenance through synthesis and techmap/aigmap passes?

  2. If not, are there any practical workflows or passes that can help me embed or extract this mapping (perhaps with attributes or a debug dump before/after major transformations)?

  3. How do others handle this problem when they need traceability from AIG or gate-level structures back to RTL or vendor primitives?

Apologies if this has been asked before. I’m a new user of Yosys and am still learning.

Any advice or pointers would be greatly appreciated.

Example screenshots from the yosys output json. Included screenshots from cells and netnames.

Not a complete answer, but maybe a useful starting point: The way most yosys passes work, it’s much easier to preserve provenance on a wire level rather than on a cell level. Even that can be far from perfect (depending on which passes you run), but it is something that is working to some degree right now and that we want to improve going forward.

Part of the reason this is easier is that passes regularly split cells (like techmap or aigmap), replace cells or even merge them (all of that for opt), but even for splitting or merging, the wires on the boundary of that rewrite remain the same without requiring any active tracking.

A very rough approach would thus be to find the wires common to the RTL and the AIG and partition both designs into non-overlapping input cones of those wires and then associate all RTL cells with all AIG cells of the corresponding input cones. This should work well for something like techmap or aigmap but I’m not sure how coarse it will become for something like opt. Part of that is inherent, if you do both splitting and merging there is no clear one-to-many correspondence anymore, but part of it is also that optimizations can introduce fresh wires that are equivalent to wires that were optimized away earlier.

There is a recover_names pass that is supposed to recover the wire names in cases where the latter happens, but I haven’t used it and have been told that it isn’t working as well as it should and probably could.

1 Like

Hello @jix, appreciate the prompt response. Indeed, I had a similar thought process, i.e. reconstructing things by following bit ids. I’ll follow up if I have any further updates or questions.