Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parcirc - parser for KLayout's gds-extracted netlist's conversion to Spectre, Spice and Xyce simulator schematic file formats #77

Merged
merged 5 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# gplugins 0.4.0
# gplugins 0.4.1

[![docs](https://github.com/gdsfactory/gplugins/actions/workflows/pages.yml/badge.svg)](https://gdsfactory.github.io/gplugins/)
[![PyPI](https://img.shields.io/pypi/v/gplugins)](https://pypi.org/project/gplugins/)
Expand All @@ -24,6 +24,7 @@ gdsfactory plugins:
- `elmer` for electrostatic (capacitive) simulations.
- `palace` for electrostatic (capacitive) simulations.
- `web` for gdsfactory webapp.
- `parcirc` for parsing GDS-extracted circuit netlists into Spice, Spectre and Xyce Schematic File formats.

## Installation

Expand Down
178 changes: 178 additions & 0 deletions gplugins/parcirc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
"""
Parcirc: A parser leveraging Dan Fritchman's
VLSIRTools for converting between
Klayout's DB Netlist format and other
electrical schematic file formats:
- SPICE
- Spectre
- Xyce
- Verilog (Not supported yet)

Author: Diogo Andre <[email protected]>
Date: Sun, 03-09-2023

TODO:
- Add support for Verilog
- Thoroughly test the parser with more complex netlists
"""

import pathlib
from itertools import count

from io import StringIO
from typing import List

from gplugins.verification.get_netlist import get_netlist
from klayout.db import (
#Pin, # Equivalent to Port
Net, # Equivalent to Signal
Netlist, # Equivalent to Package
Circuit, # Equivalent to Module
SubCircuit, # Equivalent to Instance witha Reference Module
)

from gdsfactory.typings import PathType

import vlsir.utils_pb2 as vutils
from vlsir.utils_pb2 import Reference, QualifiedName, ParamValue, Param
import vlsir.circuit_pb2 as vckt
from vlsir.circuit_pb2 import (
ExternalModule,
Module,
Signal,
Connection,
ConnectionTarget,
Port,
Instance,
Package,
SpiceType,
)

import vlsir.spice_pb2 as vsp

import vlsirtools
from vlsirtools.spice import (
SupportedSimulators,
SimOptions,
ResultFormat,
sim,
)

__SUPPORTED_FORMATS = ["spice", "spectre", "xyce", "verilog"]

def _lref(name) -> Reference:
"""Create a local `Reference` to a local `Module` with the given name."""
return Reference(local=name)

def _connections(**kwargs) -> List[Connection]:
"""Create a list of `Connection`s from keyword args of the form `portname=conn_target`, where `conn_target` is a `ConnectionTarget`."""
return [Connection(portname=key, target=value) for key, value in kwargs.items()]

def _params(**kwargs) -> List[Param]:
"""Create a list of `Param`s from keyword args of the form
`r=ParamValue(double_value=1e3)`"""
return [Param(name=key, value=value) for key, value in kwargs.items()]

def _temp_net(counter) -> str:
"""Return a new unique net name, Used for naming temporary nets."""
return f"__temp_net_{next(counter)}__"

def _net_name(net: Net, counter) -> str:
"""Get the name of a `Net`"""
if net.name is None:
return _temp_net(counter)
net_name = net.expanded_name()
return net_name.replace('$', '')

def _instance_name(instance: SubCircuit, counter) -> str:
"""Get the name of a `SubCircuit` instance"""
if instance.name is None:
return _temp_net(counter)
instance_name = instance.expanded_name()
return instance_name.replace('$', '')

def _subcircuit_instance(instance: SubCircuit, counter, **kwargs) -> Instance:
"""Create a new VLSIR Instance from the klayout.db SubCircuit and return it to include in the respective Module (SubCircuit)."""
subckt_name = _instance_name(instance, counter)
ref = instance.circuit_ref()
num_pins = ref.pin_count()
pin_nets = [instance.net_for_pin(pin_id) for pin_id in range(num_pins)]
net_names = [_net_name(net, counter) for net in pin_nets]
pin_names = [_net_name(ref.net_for_pin(pin_id), counter) for pin_id in range(num_pins)]
#pin_names = [_pin_name(ref.net_by_id(pin_id), counter) for pin_id in range(num_pins)]
# get all parent
# add the instance to the package's module
inst = Instance(
name=subckt_name,
module=_lref(ref.name),
parameters=_params(**{
prop_name: instance.property(prop_name) for prop_name in instance.property_keys()
}),
connections=_connections(**{
pin_name: ConnectionTarget(sig=net_name) for pin_name, net_name in zip(pin_names,net_names)
})
)
return inst

def _circuit_module(circuit: Circuit, counter, verbose: bool = False, **kwargs)->Module:
"""Convert a Klayout DB `Circuit` to a VLSIR 'Module' and return it to include it in the package."""
name = circuit.name
num_pins = circuit.pin_count()
pin_nets = [circuit.net_for_pin(pin_id) for pin_id in range(num_pins)]
pin_names = [_net_name(pin_net,counter) for pin_net in pin_nets]
# get all subcircuits of the circuit to form its instances
instances = [_subcircuit_instance(instance, counter) for instance in circuit.each_subcircuit()]
# FIXME: ports should have a direction, but that info is not accounted for here, rendering verilog parsing impossible
ports = [Port(direction="NONE", signal=pin_name) for pin_name in pin_names]
# add the circutit module's nets as signals to the Module
signals = [Signal(name=_net_name(net, counter), width=1) for net in circuit.each_net()]
mod = Module(
name=name,
instances=instances,
signals=signals,
ports=ports,
parameters=_params(**{
prop_name: circuit.property(prop_name) for prop_name in circuit.property_keys()
})
)

if verbose: print(mod)
return mod

def kdb_vlsir(kdbnet: Netlist, domain: str, verbose:bool = False, **kwargs) -> Package:
"""Create a VLSIR `Package` circuit netlist from a KLayout DB `Netlist`"""
_net_names_count = count() # count the number of unique net names
modules = [_circuit_module(circuit, _net_names_count, verbose=verbose) for circuit in kdbnet.each_circuit_bottom_up()]
return Package(domain=domain, modules=modules)

def export_netlist(pkg: Package, fmt: str = "spice", dest = None) -> str:
"""Export a VLSIR `Package` circuit netlist to a string in the specified format"""
assert fmt in __SUPPORTED_FORMATS, f"Unsupported format {fmt}"
if fmt == "verilog": raise NotImplementedError("Verilog export is not supported yet")
if dest is None:
dest = StringIO()
return vlsirtools.netlist(pkg=pkg, dest=dest, fmt=fmt)

#! Example usage -----
if __name__ == "__main__":

from gdsfactory.samples.demo.lvs import pads_correct

format_to_suffix = {
"spice": ".sp",
"spectre": ".scs",
"xyce": ".cir",
"verilog": ".v"
}

c = pads_correct()
gdspath = c.write_gds()
# get the netlist
kdbnetlist = get_netlist(gdspath)
# convert it to a VLSIR Package
pkg = kdb_vlsir(kdbnetlist, domain="gplugins.verification.example")
# export the netlist to the specified format
out = StringIO()
export_netlist(pkg, dest=out, fmt="spectre")
print(out.getvalue())

96 changes: 96 additions & 0 deletions gplugins/parcirc/resources/pads_correct.cir
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
; `circuit.Package` `gplugins.parcirc.example.correct_pads`
; Generated by `vlsirtools.XyceNetlister`
;

.SUBCKT straight_e0091ba5
+ 1
; No parameters

.ENDS

.SUBCKT straight_af1bc243
+ 1
; No parameters

.ENDS

.SUBCKT straight_ea93caa4
+ 1
; No parameters

.ENDS

.SUBCKT bend_euler_cross_sectionmetal3
+ 1
; No parameters

.ENDS

.SUBCKT straight_55343d8d
+ 1
; No parameters

.ENDS

.SUBCKT pad
+ 1
; No parameters

.ENDS

.SUBCKT pads_correct
; No ports
; No parameters

x1
+ tl,tr
+ pad
+ ; No parameters

x2
+ tl,tr
+ straight_ea93caa4
+ ; No parameters

x3
+ tl,tr
+ bend_euler_cross_sectionmetal3
+ ; No parameters

x4
+ tl,tr
+ straight_af1bc243
+ ; No parameters

x5
+ tl,tr
+ bend_euler_cross_sectionmetal3
+ ; No parameters

x6
+ tl,tr
+ pad
+ ; No parameters

x7
+ tl,tr
+ straight_e0091ba5
+ ; No parameters

x8
+ bl,br
+ pad
+ ; No parameters

x9
+ bl,br
+ straight_55343d8d
+ ; No parameters

x10
+ bl,br
+ pad
+ ; No parameters

.ENDS

Loading
Loading