Hardware Design Frontends#
In addition to traditional hardware description languages like Verilog and SystemVerilog, SiliconCompiler also supports higher-level languages, for ease of hardware design prototyping.
Here are some tutorials of a few different types of high-level hardware design frontends:
Python frontends#
Since SC itself is a Python library, it can be used as-is in an end-to-end build script with any Python-based HDL that can be scripted to export designs to Verilog.
For example, if you have SC installed already, you can quickly get started building designs written in the Migen HDL.
To install Migen, run pip install migen. Then, paste the following into a file called “heartbeat_migen.py”.
#!/usr/bin/env python3
import os.path
# Import the necessary components from the Migen library for hardware design.
# Migen allows you to describe hardware circuits using Python.
from migen import Module, Signal, Cat, Replicate
from migen.fhdl.verilog import convert
# Import the core classes from the siliconcompiler library.
from siliconcompiler import Design
from siliconcompiler import ASIC
# Import a pre-defined target for the FreePDK45 process.
from siliconcompiler.targets import freepdk45_demo
# --- Migen Hardware Description ---
# This class defines the 'Heartbeat' hardware module using Migen's syntax.
class Heartbeat(Module):
"""
A simple digital circuit that generates a periodic single-cycle pulse.
This module contains a counter that increments on every clock cycle.
The output 'out' goes high for a single clock cycle when the counter
reaches a specific value (1 in this case) and is low otherwise.
Args:
N (int): The bit-width of the internal counter.
"""
def __init__(self, N: int = 8):
# --- I/O Declaration ---
# Declare a 1-bit output signal for the module.
self.out = Signal()
# --- Internal Register Declaration ---
# Declare an N-bit register to be used as a counter.
self.counter_reg = Signal(N)
# COMBINATORIAL AND SEQUENTIAL LOGIC #
# The 'self.sync' domain is used to describe synchronous logic
# that updates on the positive edge of the 'sys' clock.
# On each clock cycle, increment the counter.
self.sync += self.counter_reg.eq(self.counter_reg + 1)
# Describe the logic for the output signal.
# 'Cat' concatenates signals, and 'Replicate' creates a bitstring
# of repeated values. So, Cat(Replicate(0, N-1), 1) creates the
# binary value '00...01'. The output 'out' will be high only
# when the counter is exactly 1.
self.sync += self.out.eq(self.counter_reg == Cat(Replicate(0, N - 1), 1))
def main():
"""
Generates Verilog from a Migen module and runs it through a full
ASIC compilation flow using SiliconCompiler.
"""
# --- Verilog Generation ---
# Instantiate the Migen Heartbeat module.
heartbeat = Heartbeat()
# Convert the Migen module into a Verilog file.
# The `convert` function translates the Python hardware description
# into standard Verilog HDL. We name the output module 'heartbeat'
# and write it to 'heartbeat.v'.
convert(heartbeat, ios={heartbeat.out}, name='heartbeat').write('heartbeat.v')
# --- SiliconCompiler Design Setup ---
# Create a design object to hold the configuration.
design = Design("heartbeat")
# Set the root directory for this design's source files using its filepath.
design.set_dataroot("heartbeat", __file__)
# Configure the RTL (Verilog) source files. A 'fileset' is a collection
# of files for a specific purpose.
with design.active_fileset("rtl"):
# Set the name of the top-level module.
design.set_topmodule("heartbeat")
# Add the Verilog file we just generated.
design.add_file(os.path.abspath("heartbeat.v"))
# Configure the SDC (Synopsys Design Constraints) file for timing.
# This fileset is separate from the RTL.
with design.active_dataroot("heartbeat"), design.active_fileset("sdc"):
design.add_file("heartbeat.sdc")
# --- SiliconCompiler Project Setup ---
# Create an ASIC project from the design configuration.
project = ASIC(design)
# Tell the project which filesets are needed for the compilation flow.
project.add_fileset(["rtl", "sdc"])
# Load the pre-defined target for the FreePDK45 demo process.
# This configures the project with the correct PDK, standard cell libraries,
# and tool flow for this technology.
freepdk45_demo(project)
# --- Execution & Analysis ---
# Execute the complete ASIC compilation flow (synthesis, place, route, etc.).
project.run()
# Print a summary of the results (timing, area, power, etc.).
project.summary()
# Display the final physical layout in a GDS viewer (like KLayout).
project.show()
if __name__ == '__main__':
main()
Run this file with python3 heartbeat_migen.py to compile your Migen design down to a GDS and automatically display it in KLayout.
In this example, the Heartbeat class describes a design as a Migen module, and the main() function implements the build flow.
The flow begins by using Migen’s built-in functionality for exporting the design as a file named “heartbeat.v”.
The rest of the flow uses SC’s core API to take this Verilog file and feed it into a basic asicflow build, as described in the Quickstart guide. For more info on how Migen works, see the Migen docs.
Although we wrote this example using Migen in particular, the concepts apply to other Python-based HDLs, such as MyHDL or Amaranth (note that Amaranth’s Verilog backend requires Yosys installed locally).
Chisel frontend#
SiliconCompiler has a Chisel frontend that enables you to build Chisel designs for any supported SC target. To get started using Chisel with SC, ensure that SC is installed following the directions from the Installation section, and install sbt. See the External Tools section for helpful build scripts.
To build a Chisel design, the only thing you need to do differently from a configuration perspective is: Add all required Scala files as sources. Keep in mind that other frontend-specific features such as include or library directories are not yet supported for the Chisel frontend.
Otherwise, you can configure the build as normal.
For example, to build the GCD example from the Chisel project template repo, first copy the following code into a file called “GCD.scala”.
import chisel3._
/**
* Compute GCD using subtraction method.
* Subtracts the smaller from the larger until register y is zero.
* value in register x is then the GCD
*/
class GCD extends Module {
val io = IO(new Bundle {
val value1 = Input(UInt(16.W))
val value2 = Input(UInt(16.W))
val loadingValues = Input(Bool())
val outputGCD = Output(UInt(16.W))
val outputValid = Output(Bool())
})
val x = Reg(UInt())
val y = Reg(UInt())
when(x > y) { x := x - y }
.otherwise { y := y - x }
when(io.loadingValues) {
x := io.value1
y := io.value2
}
io.outputGCD := x
io.outputValid := y === 0.U
}
This design can then be quickly compiled to a GDS using Python:
#!/usr/bin/env python3
# Import the core classes from the siliconcompiler library.
from siliconcompiler import ASIC, Design
# Import a pre-defined target for the FreePDK45 process.
from siliconcompiler.targets import freepdk45_demo
# Import a specialized flow designed to handle Chisel source files.
from siliconcompiler.flows.asicflow import ChiselASICFlow
def main():
"""
Builds the 'gcd' (Greatest Common Divisor) design using the FreePDK45 PDK.
This script demonstrates a Chisel-to-GDSII flow. It sets up an ASIC
project with a Chisel source file (.scala), loads the FreePDK45 target,
runs a specialized compilation flow to handle the Chisel to Verilog
conversion, and finally displays a summary and the final layout.
"""
# --- Design Setup ---
# Create a design object to hold the configuration.
design = Design("gcd")
# Set up a 'dataroot' to easily reference local files.
design.set_dataroot("gcd", __file__)
# Configure the "rtl" fileset. Note that the source file is Chisel (.scala).
# The ChiselASICFlow will automatically convert this to Verilog.
with design.active_dataroot("gcd"), design.active_fileset("rtl"):
# The topmodule is the name of the Chisel 'class' to be compiled.
design.set_topmodule("GCD")
design.add_file("GCD.scala")
# Configure the SDC (timing constraints) file.
with design.active_dataroot("gcd"), design.active_fileset("sdc"):
design.add_file("gcd.sdc")
# --- Project Setup ---
# Create an ASIC project from the design configuration.
project = ASIC(design)
# Tell the project which filesets are needed for the compilation flow.
project.add_fileset(["rtl", "sdc"])
# Load the pre-defined target for the FreePDK45 demo process.
freepdk45_demo(project)
# Set the project to use the ChiselASICFlow. This pre-built flow
# automatically inserts a Chisel-to-Verilog conversion step at the
# beginning, before running the standard synthesis and PnR tools.
project.set_flow(ChiselASICFlow())
# --- Execution & Analysis ---
# Execute the complete 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 (like KLayout).
project.show()
if __name__ == '__main__':
main()
For more information on creating designs using Chisel, see the Chisel docs.
C HLS frontend#
SiliconCompiler supports high-level synthesis of C code to any supported SC target, implemented using the Bambu HLS tool. To get started compiling C code with SC, ensure that SC is installed following the directions from the Installation section, and build Bambu from source. See the External Tools section for helpful build scripts.
To build a C design, the only thing you need to do differently from a configuration perspective is: Add all required C files as inputs.
Otherwise, you can configure the build as normal.
For example, to implement a GCD function as a circuit, first copy the following into a file called “gcd.c”.
#include <stdio.h>
short gcd(short a, short b) {
if (b > a) {
short tmp = a;
a = b;
b = tmp;
}
while (a != 0 && b != 0) {
short r = a % b;
a = b;
b = r;
}
if (a == 0)
return b;
else
return a;
}
int main() {
printf("gcd(4, 4) = %d\n", gcd(4, 4));
printf("gcd(27, 36) = %d\n", gcd(27, 36));
printf("gcd(270, 192) = %d\n", gcd(270, 192));
return 0;
}
This design can then be quickly compiled to a GDS using Python:
#!/usr/bin/env python3
# Copyright 2020-2025 Silicon Compiler Authors. All Rights Reserved.
from siliconcompiler import ASIC, Design
from siliconcompiler.targets import freepdk45_demo
# Import a specialized flow designed to handle High-Level Synthesis.
from siliconcompiler.flows.asicflow import HLSASICFlow
def main():
"""
Builds the 'gcd' design using the FreePDK45 PDK via a High-Level Synthesis (HLS) flow.
This script demonstrates how to take a C source file (`gcd.c`) as the primary
design input. It uses a specialized HLS flow to first synthesize the C code
into Verilog RTL, and then runs that RTL through the standard GDSII compilation
process.
"""
# --- Design Setup ---
# Create a design object to hold the configuration.
design = Design("gcd")
# Set up a 'dataroot' to easily reference local files.
design.set_dataroot("gcd", __file__)
# Configure the input fileset. While named "rtl", in an HLS flow,
# the primary source is C/C++, not Verilog.
with design.active_dataroot("gcd"), design.active_fileset("rtl"):
design.set_topmodule("gcd")
# Add the C source file as the design input.
design.add_file("gcd.c")
# Configure the SDC (timing constraints) file for the HLS-generated module.
with design.active_dataroot("gcd"), design.active_fileset("sdc"):
design.add_file("gcd_hls.sdc")
# --- Project Setup ---
# 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 FreePDK45 demo process.
freepdk45_demo(project)
# Set the project to use the HLSASICFlow. This is a pre-built flow
# that automatically inserts an HLS step (using a tool like Bambu) at the
# beginning to convert the C code to Verilog.
project.set_flow(HLSASICFlow())
# --- Execution & Analysis ---
# Execute the complete HLS-to-GDSII 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()
if __name__ == '__main__':
main()
For more information on the Bambu project used for implementing this frontend, see their docs.
Bluespec frontend#
SiliconCompiler has a Bluespec frontend that enables you to build bluespec designs for any supported SC target. To get started using Bluespec with SC, ensure that SC is installed following the directions from the Installation section, and download bsc or install it from source following the directions here. See the External Tools section for helpful build scripts.
To build a Bluespec design, the only thing you need to do differently from a configuration perspective is:
Add the Bluespec top-level package as an ‘input’, and add all directories containing imported modules as entries in [fileset,<fileset>,idir].
Otherwise, you can configure the build as normal.
For example, to build this fibonacci example adapted from the bsc smoke test, first copy the following code into a file called “FibOne.bsv”.
interface FibOne_IFC;
method Action nextFib;
method ActionValue #(int) getFib;
endinterface
(* synthesize *)
module mkFibOne(FibOne_IFC);
// register containing the current Fibonacci value
Reg#(int) this_fib <- mkReg (0);
Reg#(int) next_fib <- mkReg (1);
method Action nextFib;
this_fib <= next_fib;
next_fib <= this_fib + next_fib; // note that this uses stale this_fib
endmethod
method ActionValue#(int) getFib;
return this_fib;
endmethod
endmodule: mkFibOne
This design can then be quickly compiled to a GDS using Python:
#!/usr/bin/env python3
# Import the core classes from the siliconcompiler library.
from siliconcompiler import ASIC, Design
# Import a pre-defined target for the FreePDK45 process.
from siliconcompiler.targets import freepdk45_demo
# Import the base ASIC flow to customize it.
from siliconcompiler.flows.asicflow import ASICFlow
# Import the specific tool task for compiling Bluespec.
from siliconcompiler.tools.bluespec import convert
def main():
'''
This script demonstrates a Bluespec SystemVerilog (BSV) to GDSII flow.
It shows how to create a custom compilation flow in SiliconCompiler
to handle high-level synthesis from BSV before proceeding with the
standard synthesis, place, and route steps.
'''
# --- Design Setup ---
# Create a design schema to hold the project's configuration.
design = Design("fibone")
# Set up a 'dataroot' to easily reference local files.
design.set_dataroot("fibone", __file__)
# Configure the "rtl" fileset.
# Note that the source file is a '.bsv' file, not Verilog.
with design.active_dataroot("fibone"), design.active_fileset("rtl"):
# The topmodule 'mkFibOne' is the name of the compiled module
# generated by the Bluespec compiler, not the file name.
design.set_topmodule("mkFibOne")
design.add_file("FibOne.bsv")
# --- Project Setup ---
# Create a standard ASIC project.
project = ASIC(design)
# Tell the project to use the "rtl" fileset we defined.
project.add_fileset("rtl")
# Load the target configuration for the FreePDK45 technology.
freepdk45_demo(project)
# --- Custom Flow Definition ---
# This is the key part of the example. We will modify the standard
# ASIC flow to include a Bluespec compilation step.
# 1. Start with a copy of the standard ASIC flow.
flow = ASICFlow("asic-bluespec")
# 2. The standard flow has an 'elaborate' step for SystemVerilog that is
# not needed, as the Bluespec compiler handles elaboration. Remove it.
flow.remove_node("elaborate")
# 3. Add a new step (node) to the flow graph named "convert".
# This step will run the Bluespec compiler (bsc) to convert the
# .bsv source into Verilog.
flow.node("convert", convert.ConvertTask())
# 4. Modify the flow graph's connections (edges). Reroute the flow so
# that the output of our new "convert" step feeds into the "synthesis" step.
flow.edge("convert", "synthesis")
# 5. Apply this new, custom flow to our project.
project.set_flow(flow)
# --- Execution & Analysis ---
# Run the custom flow. SiliconCompiler will now execute the 'convert'
# step first, then proceed to synthesis, place, route, etc.
project.run()
# Display a summary of the results (timing, area, etc.).
project.summary()
return project
if __name__ == '__main__':
main()
For more information on creating designs using Bluespec, see the Bluespec docs.