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-based 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

from migen import Module, Signal, Cat, Replicate
from migen.fhdl.verilog import convert

import siliconcompiler


class Heartbeat(Module):
    def __init__(self, N=8):
        self.out = Signal()
        self.counter_reg = Signal(N)

        ###

        self.sync += self.counter_reg.eq(self.counter_reg + 1)
        self.sync += self.out.eq(self.counter_reg == Cat(Replicate(0, N - 1), 1))


def main():
    heartbeat = Heartbeat()
    convert(heartbeat, ios={heartbeat.out}, name='heartbeat').write('heartbeat.v')

    chip = siliconcompiler.Chip('heartbeat')
    chip.input('heartbeat.v')
    # default Migen clock pin is named 'sys_clk'
    chip.clock(pin='sys_clk', period=1)
    chip.load_target("freepdk45_demo")
    chip.run()
    chip.summary()
    chip.show()


if __name__ == '__main__':
    main()

Run this file with python 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 for links to helpful build scripts.

To build a Chisel design, the only things you need to do differently from a configuration perspective are:

  1. 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.

  2. Set the ['option', 'frontend'] parameter to ‘chisel’.

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
}

Note

SC’s Chisel driver script selects the module to build based on the ‘design’ parameter. You must ensure that top-level module’s class name matches the ‘design’ parameter you have set, and that this module does not include a package statement.

This design can then be quickly compiled to a GDS using Python:

#!/usr/bin/env python3

import siliconcompiler
import os


def main():
    root = os.path.dirname(__file__)
    chip = siliconcompiler.Chip('GCD')
    chip.input(os.path.join(root, "GCD.scala"))
    chip.set('option', 'frontend', 'chisel')
    # default Chisel clock pin is 'clock'
    chip.clock(pin='clock', period=5)
    chip.load_target("freepdk45_demo")
    chip.run()
    chip.summary()
    chip.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 for links to helpful build scripts.

To build a C design, the only things you need to do differently from a configuration perspective are:

  1. Add all required C files as inputs.

  2. Set the ['option', 'frontend'] parameter to ‘c’.

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;
}

Note

SC’s C frontend driver script selects a function to implement as a Verilog module using the ‘design’ parameter. Ensure that your C code includes a function that matches the value stored in ‘design’.

This design can then be quickly compiled to a GDS using Python:

#!/usr/bin/env python3

import siliconcompiler
import os


def main():
    root = os.path.dirname(__file__)
    chip = siliconcompiler.Chip('gcd')
    chip.input(os.path.join(root, "gcd.c"))
    chip.set('option', 'frontend', 'c')
    # default Bambu clock pin is 'clock'
    chip.clock(pin='clock', period=5)
    chip.load_target("freepdk45_demo")
    chip.run()
    chip.summary()
    chip.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 for links to helpful build scripts.

To build a Bluespec design, the only things you need to do differently from a configuration perspective are:

  1. Add the Bluespec top-level package as an ‘input’, and add all directories containing imported modules as entries in ['option', 'ydir']. Keep in mind that the Bluespec integration only supports specifying a single top-level source file, so you must use ['option', 'ydir'] for all other sources.

  2. Set the ['option', 'frontend'] parameter to ‘bluespec’.

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

Note

SC’s Bluespec driver script selects the module to build based on the ‘design’ parameter. You must ensure that the single file passed in via the ‘source’ parameter contains a module name that matches the value in ‘design’.

This design can then be quickly compiled to a GDS using Python:

#!/usr/bin/env python3

import siliconcompiler
import os


def main():
    root = os.path.dirname(__file__)
    chip = siliconcompiler.Chip('mkFibOne')
    chip.input(os.path.join(root, "FibOne.bsv"))
    chip.set('option', 'frontend', 'bluespec')
    # default Bluespec clock pin is 'CLK'
    chip.clock(pin='CLK', period=5)
    chip.load_target("freepdk45_demo")
    chip.run()
    chip.summary()
    chip.show()


if __name__ == '__main__':
    main()

For more information on creating designs using Bluespec, see the Bluespec docs.