Migrating from the Chip API#
If you have a script that starts like this, you are using an API that no longer exists:
from siliconcompiler import Chip
chip = Chip('heartbeat')
chip.input('heartbeat.v')
chip.load_target('freepdk45_demo')
chip.run()
The Chip class was removed in v0.35.0 (October 2025) and replaced by a
Design object plus a project object. This page maps the
old names onto the new ones.
Note
This page is kept indefinitely, and deliberately spells out the old names in
full. Years of tutorials, papers, forum answers and blog posts use the
Chip API, search engines still surface documentation for releases that
predate the change, and code assistants trained on all of it will happily
write Chip('mydesign') today. If you arrived here from one of those, you
are in the right place.
Parameter-level changes – keys added, renamed or removed inside the schema itself – are recorded separately in Schema Changes.
Why it changed#
Chip was one object holding two unrelated things: what you are building
and how this particular build is configured. That made a design impossible to
reuse – to build the same RTL for two processes, or reuse a block inside a
larger chip, you rebuilt the object from scratch.
The replacement separates them:
A
Designdescribes source code: files, grouped into filesets, with a top module and include paths. It says nothing about a process or a flow, so the sameDesigncan be built many ways, and one design can depend on another.A project describes one compilation of a design. Use the class that matches the job –
ASIC,FPGA,LintorSim– which is what replaces the oldoption,modeflag. Each one brings the schema, constraints and metrics for its domain, so anASICcarries theasic,*parameters that aLinthas no use for.Projectis the base class they extend, and is not the one to write a build script against: it has no domain section, so an ASIC target applied to a bareProjectfails withAttributeError.
The second change is that configuration moved from string keypaths to typed
accessors – project.option.set_remote(True) rather than
chip.set('option', 'remote', True). Keypaths still work and are still the
layer underneath; see Working with the Schema for when to
use which.
The same script, before and after#
The old form, from the heartbeat example as it shipped in v0.34.3:
from siliconcompiler import Chip
from siliconcompiler.targets import freepdk45_demo
chip = Chip('heartbeat')
chip.register_source("heartbeat-example", __file__)
chip.input("heartbeat.v", package="heartbeat-example")
chip.input("heartbeat.sdc", package="heartbeat-example")
chip.use(freepdk45_demo)
chip.run()
chip.summary()
chip.show()
The same build today. It is pulled in from examples/heartbeat/heartbeat.py,
so it is exercised by the test suite rather than transcribed here:
design = Design("heartbeat")
# Set the root directory for the design's source files.
design.set_dataroot("heartbeat", __file__)
# Configure the RTL (Verilog) source files.
design.set_topmodule("heartbeat", fileset="rtl")
design.add_file("heartbeat.v", dataroot="heartbeat", fileset="rtl")
# Configure the SDC (timing constraints) file.
design.add_file("heartbeat.sdc", dataroot="heartbeat", fileset="sdc")
# Create an ASIC project from the design configuration.
project = ASIC(design)
# Enable the necessary filesets for the compilation flow.
project.add_fileset(["rtl", "sdc"])
# Load the pre-defined target for the Skywater130 demo process.
skywater130_demo(project)
# Execute the compilation flow.
project.run()
# Print a summary of the results (timing, area, power, etc.).
project.summary()
# Display the final physical layout in a GDS viewer.
project.show()
Four differences to notice, because they account for most of the porting work:
Two objects instead of one.
Designfor the sources,ASICfor the build.Files carry a fileset.
input()guessed a file’s role from its extension;add_file()requires you to name it, and the project then selects which filesets to compile withadd_fileset().The top module is explicit. It used to be inferred from the
Chipname.A target is called, not “used”.
skywater130_demo(project)instead ofchip.use(...)orchip.load_target(...).
Objects and imports#
Removed |
Use instead |
|---|---|
|
|
|
|
|
|
|
|
|
Nothing to import. A project is a schema – |
|
The |
|
Standard Python exceptions |
Warning
FPGA still exists and means something different. It used to be the
FPGA device class; it is now the FPGA project class. The device is
FPGADevice. Old code reading
FPGA('mydevice') needs FPGADevice('mydevice').
Setting up a design#
Old |
New |
|---|---|
|
|
|
|
|
|
|
|
|
Outputs are produced by tasks, not declared on the design |
|
|
|
|
|
|
|
|
|
|
|
Removed with no direct equivalent – the |
|
|
|
|
Configuring and running#
Old |
New |
|---|---|
|
|
|
Import the target and call it. The string form, and the |
|
Use the |
|
|
|
|
|
|
|
|
|
|
|
|
|
Unchanged – same names on the project |
|
|
|
The CLI dashboard now runs during |
|
|
|
Unchanged in name, now provided by |
|
Raise an exception, or log through |
Reading results and paths#
Old |
New |
|---|---|
|
Unchanged, but read it from the object |
|
|
|
|
|
|
|
Unchanged – same names on the project |
|
|
|
Unchanged in name, now schema methods; |
|
Removed. Parameter help text is in the
Schema Reference, or |
|
|
Building flows, tools and libraries#
Module-style setup functions became classes. Old flows, tool tasks, PDKs and
libraries were modules exposing a setup(chip) function plus a make_docs
hook, discovered by import:
# old: siliconcompiler/flows/myflow.py
def make_docs(chip):
return setup()
def setup(flowname='myflow'):
flow = siliconcompiler.Flow(flowname)
flow.node(flowname, 'import', parse)
flow.node(flowname, 'syn', syn_asic)
flow.edge(flowname, 'import', 'syn')
return flow
Now each is a class, and make_docs is gone – the documentation is generated
from the class and its docstring:
# new
from siliconcompiler import Flowgraph
class MyFlow(Flowgraph):
'''One-line summary, which becomes the description in the docs.'''
def __init__(self):
super().__init__("myflow")
self.node("elaborate", Elaborate())
self.node("synthesis", ASICSynthesis())
self.edge("elaborate", "synthesis")
The same shape applies elsewhere: a tool task subclasses
Task instead of exposing setup(chip),
pre_process(chip) and post_process(chip) module functions; a standard
cell library subclasses StdCellLibrary; a PDK
subclasses PDK. Nodes take a task instance rather
than a module reference, and node()/edge() are methods on the flowgraph
rather than on the chip.
Targets are the exception: they are still functions taking a project, because a target’s job is to configure one.
See the Development Guide for how to write each of these, and Where your module belongs before you decide which repository it goes in – that answer also changed.
The command line#
There is no sc command, and there never was one after v0.35.0. The
entry points are sc-dashboard, sc-issue, sc-remote, sc-server,
sc-show, sc-install and smake.
Old |
New |
|---|---|
|
Write a Python script – see the example above – or use |
|
|
|
|
Everything else – sc-show, sc-issue, sc-remote and the rest –
kept its name and its arguments.
Still stuck?#
If a symbol is not listed here, two places are worth checking before asking: Schema Changes for anything that looks like a schema key, and the Python API for a method name. Failing that, ask in Discussions – and please say which version the old script targeted, because it tells us which era of the API to translate from.