adding physical layout scripts
This commit is contained in:
parent
9a216710a1
commit
2cea2debdb
4
rect2gds.sh
Executable file
4
rect2gds.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
./rect2lef.py -Tsky130 $1.rect $1.lef layermap.txt
|
||||
strm2gds -d 20 --lefdef-map layermap.txt $1.lef $1.gds
|
241
rect2lef.py
Executable file
241
rect2lef.py
Executable file
@ -0,0 +1,241 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
def stripComments(line):
|
||||
escape = False
|
||||
inString = False
|
||||
for i, c in enumerate(line):
|
||||
if not escape and (c == "\"" or c == "\'"):
|
||||
inString = not inString
|
||||
elif inString and c == "\\":
|
||||
escape = not escape
|
||||
elif not inString and c == "#":
|
||||
return line[0:i]
|
||||
else:
|
||||
escape = False
|
||||
return line
|
||||
|
||||
def loadActConf(path):
|
||||
result = dict()
|
||||
stack = [result]
|
||||
with open(path, "r") as fptr:
|
||||
for number, line in enumerate(fptr):
|
||||
if "#" in line:
|
||||
line = line[0:line.index("#")]
|
||||
args = [arg.strip() for arg in line.strip().split(" ")]
|
||||
if len(args) > 0:
|
||||
if args[0] == "include":
|
||||
result = result | loadActConf(args[1][1:-1])
|
||||
elif args[0] == "begin":
|
||||
stack[-1][args[1]] = dict()
|
||||
stack.append(stack[-1][args[1]])
|
||||
elif args[0] == "end":
|
||||
stack.pop()
|
||||
elif args[0] == "string":
|
||||
stack[-1][args[1]] = args[2][1:-1]
|
||||
elif args[0] == "int":
|
||||
stack[-1][args[1]] = int(args[2])
|
||||
elif args[0] == "real":
|
||||
stack[-1][args[1]] = float(args[2])
|
||||
elif args[0] == "int_table":
|
||||
stack[-1][args[1]] = [int(arg) for arg in args[2:]]
|
||||
elif args[0] == "string_table":
|
||||
stack[-1][args[1]] = [arg[1:-1] for arg in args[2:]]
|
||||
return result
|
||||
|
||||
def queryGDS(conf, rectLayer):
|
||||
gds = []
|
||||
gds_bloat = []
|
||||
if rectLayer in conf["materials"]:
|
||||
if "gds" in conf["materials"][rectLayer]:
|
||||
gds = conf["materials"][rectLayer]["gds"]
|
||||
if "gds_bloat" in conf["materials"][rectLayer]:
|
||||
gds_bloat = conf["materials"][rectLayer]["gds_bloat"]
|
||||
else:
|
||||
if rectLayer+"_gds" in conf["materials"]["metal"]:
|
||||
gds = conf["materials"]["metal"][rectLayer+"_gds"]
|
||||
if rectLayer+"_gds_bloat" in conf["materials"]["metal"]:
|
||||
gds_bloat = conf["materials"]["metal"][rectLayer+"_gds_bloat"]
|
||||
return zip(gds, gds_bloat)
|
||||
|
||||
TAP, FILL, CELL, BLOCK = range(4)
|
||||
|
||||
class Rect:
|
||||
def __init__(self, label, layer, bounds, hint="", isInput=False, isOutput=False):
|
||||
self.label = label
|
||||
self.layer = layer
|
||||
self.bounds = [int(bound) for bound in bounds]
|
||||
self.hint = hint
|
||||
self.isInput = isInput
|
||||
self.isOutput = isOutput
|
||||
|
||||
def isIn(searches, string):
|
||||
for search in searches:
|
||||
if search in string:
|
||||
return True
|
||||
return False
|
||||
|
||||
class Cell:
|
||||
def __init__(self, name, kind, bbox, rects):
|
||||
self.name = name
|
||||
self.kind = kind
|
||||
self.bbox = bbox
|
||||
self.rects = rects
|
||||
|
||||
def readCell(path):
|
||||
name = os.path.splitext(os.path.basename(path))[0]
|
||||
kind = BLOCK
|
||||
if "welltap" in name.lower():
|
||||
kind = TAP
|
||||
elif "fill" in name.lower():
|
||||
kind = FILL
|
||||
elif "cell" in name.lower():
|
||||
kind = CELL
|
||||
|
||||
# left, bottom, right, top
|
||||
bbox = [0, 0, 1, 1]
|
||||
rects = []
|
||||
|
||||
with open(path, "r") as rf:
|
||||
for number, line in enumerate(rf):
|
||||
args = [arg.strip() for arg in line.split(" ")]
|
||||
if args[0] == "bbox":
|
||||
bbox = [int(arg) for arg in args[1:]]
|
||||
else:
|
||||
isInput = (args[0] == "inrect")
|
||||
isOutput = (args[0] == "outrect")
|
||||
hint = ""
|
||||
if len(args) >= 8:
|
||||
hint = args[7]
|
||||
|
||||
rects.append(Rect(args[1], args[2], [int(arg) for arg in args[3:7]], hint, isInput, isOutput))
|
||||
return Cell(name, kind, bbox, rects)
|
||||
|
||||
def writeLayerMap(path, conf):
|
||||
layers = zip(conf["gds"]["layers"], conf["gds"]["major"], conf["gds"]["minor"])
|
||||
with open(path, "w") as fptr:
|
||||
for layer in layers:
|
||||
name, purpose = layer[0].rsplit(".", 1)
|
||||
if "via" in name and purpose in ["drawing", "dg", "drw"]:
|
||||
print(f"{name} VIA {layer[1]} {layer[2]}", file=fptr)
|
||||
elif purpose in ["drawing", "dg", "drw"]:
|
||||
print(f"{name} LEFOBS {layer[1]} {layer[2]}", file=fptr)
|
||||
elif purpose in ["label", "ll", "lbl"]:
|
||||
print(f"NAME {name}/PINNAME {layer[1]} {layer[2]}", file=fptr)
|
||||
print(f"NAME {name}/PIN {layer[1]} {layer[2]}", file=fptr)
|
||||
print(f"NAME {name}/LEFPINNAME {layer[1]} {layer[2]}", file=fptr)
|
||||
print(f"NAME {name}/LEFPIN {layer[1]} {layer[2]}", file=fptr)
|
||||
elif purpose in ["net", "nt"]:
|
||||
print(f"{name} NET {layer[1]} {layer[2]}", file=fptr)
|
||||
elif purpose in ["pin", "pin1", "pn"]:
|
||||
print(f"{name} PIN,LEFPIN {layer[1]} {layer[2]}", file=fptr)
|
||||
elif purpose in ["blockage", "be", "blo"]:
|
||||
print(f"{name} BLOCKAGE {layer[1]} {layer[2]}", file=fptr)
|
||||
if "prb" in name.lower():
|
||||
print(f"DIEAREA ALL {layer[1]} {layer[2]}", file=fptr)
|
||||
|
||||
def writeLEF(path, conf, cell):
|
||||
scale = conf["general"]["scale"]
|
||||
numMetals = conf["general"]["metals"]
|
||||
with open(path, "w") as fptr:
|
||||
print(f"MACRO {cell.name}", file=fptr)
|
||||
if cell.kind == TAP:
|
||||
print("CLASS CORE WELLTAP ;", file=fptr)
|
||||
elif cell.kind == FILL:
|
||||
print("CLASS CORE SPACER ;", file=fptr)
|
||||
elif cell.kind == CELL:
|
||||
print("CLASS CORE ;", file=fptr)
|
||||
else:
|
||||
print("CLASS BLOCK ;", file=fptr)
|
||||
|
||||
print(f"\tORIGIN {-cell.bbox[0]*scale} {-cell.bbox[1]*scale} ;", file=fptr)
|
||||
print(f"\tFOREIGN {cell.name} {cell.bbox[0]*scale} {cell.bbox[1]*scale} ;", file=fptr)
|
||||
print(f"\tSIZE {(cell.bbox[2]-cell.bbox[0])*scale} BY {(cell.bbox[3]-cell.bbox[1])*scale} ;", file=fptr)
|
||||
print(f"\tSYMMETRY X Y ;", file=fptr)
|
||||
if cell.kind in [FILL, TAP, CELL]:
|
||||
print("\tSITE CoreSite ;", file=fptr)
|
||||
|
||||
for rect in cell.rects:
|
||||
if rect.isInput or rect.isOutput:
|
||||
direction = "INOUT"
|
||||
if not rect.isInput:
|
||||
direction = "OUTPUT"
|
||||
elif not rect.isOutput:
|
||||
direction = "INPUT"
|
||||
|
||||
print(f"\tPIN {rect.label}", file=fptr)
|
||||
if isIn(["vnsub", "vpsub", "vddsub", "vsssub", "vddb", "vssb"], rect.label.lower()):
|
||||
print("\t\tDIRECTION INOUT ;", file=fptr)
|
||||
print("\t\tUSE POWER ;", file=fptr)
|
||||
elif isIn(["vdd", "pwr"], rect.label.lower()):
|
||||
print("\t\tDIRECTION INOUT ;", file=fptr)
|
||||
print("\t\tUSE POWER ;", file=fptr)
|
||||
if cell.kind in [FILL, TAP, CELL]:
|
||||
print("\t\tSHAPE ABUTMENT ;", file=fptr)
|
||||
elif isIn(["gnd", "vss"], rect.label.lower()):
|
||||
print("\t\tDIRECTION INOUT ;", file=fptr)
|
||||
print("\t\tUSE GROUND ;", file=fptr)
|
||||
if cell.kind in [FILL, TAP, CELL]:
|
||||
print("\t\tSHAPE ABUTMENT ;", file=fptr)
|
||||
else:
|
||||
print(f"\t\tDIRECTION {direction} ;", file=fptr)
|
||||
print(f"\t\tUSE SIGNAL ;", file=fptr)
|
||||
|
||||
print("\t\tPORT", file=fptr)
|
||||
gds = queryGDS(conf, rect.layer)
|
||||
for layer, bloat in gds:
|
||||
name, purpose = layer.rsplit(".", 1)
|
||||
print(f"\t\t\tLAYER {name} ;", file=fptr)
|
||||
print(f"\t\t\t\tRECT {(rect.bounds[0]-bloat)*scale} {(rect.bounds[1]-bloat)*scale} {(rect.bounds[2]+bloat)*scale} {(rect.bounds[3]+bloat)*scale} ;", file=fptr)
|
||||
print("\t\tEND", file=fptr)
|
||||
print(f"\tEND {rect.label}", file=fptr)
|
||||
|
||||
print("\tOBS", file=fptr)
|
||||
for rect in cell.rects:
|
||||
#if not rect.isInput and not rect.isOutput:
|
||||
gds = queryGDS(conf, rect.layer)
|
||||
for layer, bloat in gds:
|
||||
name, purpose = layer.rsplit(".", 1)
|
||||
print(f"\t\tLAYER {name} ;", file=fptr)
|
||||
print(f"\t\t\tRECT {(rect.bounds[0]-bloat)*scale} {(rect.bounds[1]-bloat)*scale} {(rect.bounds[2]+bloat)*scale} {(rect.bounds[3]+bloat)*scale} ;", file=fptr)
|
||||
print("\tEND", file=fptr)
|
||||
print(f"END {cell.name}", file=fptr)
|
||||
print("", file=fptr)
|
||||
|
||||
def print_help():
|
||||
print("Usage: rect2lef.py [options] <input.rect> <output.lef> [output.layermap]")
|
||||
print("\t-T<tech>\tidentify the technology used for this translation.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) <= 2 or sys.argv[1] == '--help' or sys.argv[1] == '-h':
|
||||
print_help()
|
||||
else:
|
||||
rectPath = None
|
||||
lefPath = None
|
||||
lmPath = None
|
||||
techName = "sky130"
|
||||
actHome = os.environ.get('ACT_HOME', "/opt/cad")
|
||||
for arg in sys.argv[1:]:
|
||||
if arg[0] == '-':
|
||||
if arg[1] == 'T':
|
||||
techName = arg[2:]
|
||||
else:
|
||||
print(f"error: unrecognized option '{arg}'")
|
||||
print("")
|
||||
print_help()
|
||||
sys.exit()
|
||||
elif not rectPath:
|
||||
rectPath = arg
|
||||
elif not lefPath:
|
||||
lefPath = arg
|
||||
elif not lmPath:
|
||||
lmPath = arg
|
||||
|
||||
conf = loadActConf(actHome + "/conf/" + techName + "/layout.conf")
|
||||
if rectPath and lefPath:
|
||||
cell = readCell(rectPath)
|
||||
writeLEF(cell.name + ".lef", conf, cell)
|
||||
if lmPath:
|
||||
writeLayerMap(lmPath, conf)
|
547
spi2act.py
Executable file
547
spi2act.py
Executable file
@ -0,0 +1,547 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import sys
|
||||
from math import trunc
|
||||
|
||||
# lvtnfet d g s b
|
||||
#
|
||||
class Transistor:
|
||||
def __init__(self,
|
||||
inst,
|
||||
kind,
|
||||
gate,
|
||||
source,
|
||||
drain,
|
||||
bulk,
|
||||
width = 1,
|
||||
length = 1):
|
||||
self.inst = inst
|
||||
self.kind = kind
|
||||
self.gate = gate
|
||||
self.source = source
|
||||
self.drain = drain
|
||||
self.bulk = bulk
|
||||
self.width = width
|
||||
self.length = length
|
||||
self.shared_source = list()
|
||||
self.shared_drain = list()
|
||||
|
||||
def emit(self, sources, tab = ''):
|
||||
if self.drain in sources or self.drain[0] == '@':
|
||||
self.flip()
|
||||
|
||||
if self.source in sources:
|
||||
if self.kind == 'n':
|
||||
fmt = tab + '{gate}<{width:.1f},{length:.1f}> -> {drain}-'
|
||||
elif self.kind == 'p':
|
||||
fmt = tab + '~{gate}<{width:.1f},{length:.1f}> -> {drain}+'
|
||||
elif self.source[0] == '@':
|
||||
if self.kind == 'n':
|
||||
fmt = tab + '~{source} & {gate}<{width:.1f},{length:.1f}> -> {drain}-'
|
||||
elif self.kind == 'p':
|
||||
fmt = tab + '{source} & ~{gate}<{width:.1f},{length:.1f}> -> {drain}+'
|
||||
else:
|
||||
fmt = tab + 'pass{kind}<{width:.1f},{length:.1f}>({gate}, {source}, {drain})'
|
||||
#fmt = tab + '{gate}<{width:.1f},{length:.1f}> -> {drain} := {source}'
|
||||
|
||||
return fmt.format(
|
||||
kind=self.kind,
|
||||
width=trunc(self.width),
|
||||
length=trunc(self.length),
|
||||
gate=self.gate,
|
||||
source=self.source,
|
||||
drain=self.drain)
|
||||
|
||||
|
||||
def emit_expr(self):
|
||||
return '{kind}{gate}<{width:.1f},{length:.1f}>'.format(
|
||||
kind = '~' if self.kind == 'p' else '',
|
||||
gate = self.gate,
|
||||
width = trunc(self.width),
|
||||
length = trunc(self.length))
|
||||
|
||||
def ports(self):
|
||||
result = [self.bulk, self.gate]
|
||||
if self.source[0] != '@':
|
||||
result.append(self.source)
|
||||
if self.drain[0] != '@':
|
||||
result.append(self.drain)
|
||||
|
||||
return list(set(result))
|
||||
|
||||
def gates(self):
|
||||
return [self.gate]
|
||||
|
||||
def flip(self):
|
||||
self.source,self.drain = self.drain,self.source
|
||||
self.shared_source,self.shared_drain = self.shared_drain,self.shared_source
|
||||
|
||||
class Expr:
|
||||
def __init__(self,
|
||||
op,
|
||||
kind,
|
||||
source,
|
||||
drain,
|
||||
bulk = '',
|
||||
devs = None):
|
||||
self.op = op
|
||||
self.kind = kind
|
||||
self.source = source
|
||||
self.drain = drain
|
||||
self.bulk = bulk
|
||||
if devs:
|
||||
self.devs = devs
|
||||
else:
|
||||
self.devs = list()
|
||||
self.shared_source = list()
|
||||
self.shared_drain = list()
|
||||
|
||||
def emit_expr(self):
|
||||
return (' ' + self.op + ' ').join([
|
||||
'(' + dev.emit_expr() + ')'
|
||||
if isinstance(dev, Expr) and dev.op == '|'
|
||||
else dev.emit_expr()
|
||||
for dev in self.devs])
|
||||
|
||||
def emit(self, sources, tab = ''):
|
||||
if self.drain in sources or self.drain[0] == '@':
|
||||
self.flip()
|
||||
|
||||
if self.source in sources:
|
||||
if self.kind == 'n':
|
||||
return tab + self.emit_expr() + ' -> ' + self.drain + '-'
|
||||
elif self.kind == 'p':
|
||||
return tab + self.emit_expr() + ' -> ' + self.drain + '+'
|
||||
elif self.source[0] == '@':
|
||||
if self.kind == 'n':
|
||||
return tab + '~' + self.source + ' & (' + self.emit_expr() + ') -> ' + self.drain + '-'
|
||||
elif self.kind == 'p':
|
||||
return tab + self.source + ' & (' + self.emit_expr() + ') -> ' + self.drain + '+'
|
||||
else:
|
||||
return '\n'.join([dev.emit(sources, tab) for dev in self.devs])
|
||||
#return tab + self.emit_expr() + ' -> ' + self.drain + ' := ' + self.source
|
||||
|
||||
def gates(self):
|
||||
result = list()
|
||||
for dev in self.devs:
|
||||
result += dev.gates()
|
||||
return list(set(result))
|
||||
|
||||
def ports(self):
|
||||
result = [self.bulk]
|
||||
if self.source[0] != '@':
|
||||
result.append(self.source)
|
||||
if self.drain[0] != '@':
|
||||
result.append(self.drain)
|
||||
|
||||
result += self.gates()
|
||||
|
||||
return list(set(result))
|
||||
|
||||
def flip(self):
|
||||
self.source,self.drain = self.drain,self.source
|
||||
self.shared_source,self.shared_drain = self.shared_drain,self.shared_source
|
||||
for dev in self.devs:
|
||||
dev.flip()
|
||||
self.devs.reverse()
|
||||
|
||||
class Instance:
|
||||
def __init__(self,
|
||||
name,
|
||||
typename,
|
||||
ports = None):
|
||||
self.name = name
|
||||
self.typename = typename
|
||||
if ports:
|
||||
self.ports = ports
|
||||
else:
|
||||
self.ports = list()
|
||||
|
||||
def emit(self, tab = ''):
|
||||
if self.ports:
|
||||
return tab + '{typename} {name}({ports});'.format(
|
||||
typename = self.typename,
|
||||
name = self.name,
|
||||
ports = ', '.join(self.ports))
|
||||
else:
|
||||
return tab + self.typename + ' ' + self.name + ';'
|
||||
|
||||
def can_merge(left, right, sources):
|
||||
return left == right or (left in sources or left and left[0] == '@') and (right in sources or right and right[0] == '@')
|
||||
|
||||
class Process:
|
||||
def __init__(self,
|
||||
name,
|
||||
ports = None,
|
||||
nets = None,
|
||||
devs = None,
|
||||
use_globals = False):
|
||||
self.name = name
|
||||
self.use_globals = use_globals
|
||||
if self.use_globals:
|
||||
self.vdd = 'g.Vdd'
|
||||
self.gnd = 'g.GND'
|
||||
self.vdds = 'g.vpsub'
|
||||
self.gnds = 'g.vnsub'
|
||||
else:
|
||||
self.vdd = None
|
||||
self.gnd = None
|
||||
self.vdds = None
|
||||
self.gnds = None
|
||||
|
||||
self.ports = list()
|
||||
self.add_ports(ports)
|
||||
self.nets = list()
|
||||
self.add_nets(nets)
|
||||
self.devs = list()
|
||||
self.add_devs(devs)
|
||||
self.insts = list()
|
||||
|
||||
@property
|
||||
def sources(self):
|
||||
return [x for x in [self.vdd, self.gnd, self.vdds, self.gnds] if not x is None]
|
||||
|
||||
def check(self, names):
|
||||
if names is not None:
|
||||
if isinstance(names, list):
|
||||
result = list()
|
||||
for name in names:
|
||||
result.extend(self.check(name))
|
||||
return result
|
||||
else:
|
||||
if 'vdds' in names.lower() or 'vpb' in names.lower() or 'vpsub' in names.lower() or names == self.vdds:
|
||||
if not self.vdds:
|
||||
self.vdds = names
|
||||
return [self.vdds]
|
||||
elif 'gnds' in names.lower() or 'vnb' in names.lower() or 'vnsub' in names.lower() or names == self.gnds:
|
||||
if not self.gnds:
|
||||
self.gnds = names
|
||||
return [self.gnds]
|
||||
elif 'vdd' in names.lower() or 'pwr' in names.lower() or names == self.vdd:
|
||||
if not self.vdd:
|
||||
self.vdd = names
|
||||
return [self.vdd]
|
||||
elif 'gnd' in names.lower() or names == self.gnd:
|
||||
if not self.gnd:
|
||||
self.gnd = names
|
||||
return [self.gnd]
|
||||
else:
|
||||
return [names.replace("#","")]
|
||||
return []
|
||||
|
||||
def add_ports(self, ports):
|
||||
if self.use_globals:
|
||||
self.ports.extend([x for x in self.check(ports) if x not in self.sources])
|
||||
else:
|
||||
self.ports.extend(self.check(ports))
|
||||
|
||||
def add_nets(self, nets):
|
||||
self.nets.extend([x for x in self.check(nets) if x not in self.sources])
|
||||
|
||||
def check_devs(self, devs):
|
||||
if devs is not None:
|
||||
if isinstance(devs, list):
|
||||
result = list()
|
||||
for dev in devs:
|
||||
result.extend(self.check_devs(dev))
|
||||
return result
|
||||
else:
|
||||
devs.source = self.check(devs.source)[0]
|
||||
devs.drain = self.check(devs.drain)[0]
|
||||
devs.bulk = self.check(devs.bulk)[0]
|
||||
if isinstance(devs, Transistor):
|
||||
devs.gate = self.check(devs.gate)[0]
|
||||
if isinstance(devs, Expr):
|
||||
devs.devs = self.check_devs(devs.devs)
|
||||
return [devs]
|
||||
return []
|
||||
|
||||
def add_devs(self, devs):
|
||||
self.devs.extend(self.check_devs(devs))
|
||||
|
||||
def add_insts(self, insts):
|
||||
if insts is not None:
|
||||
if isinstance(insts, list):
|
||||
result = list()
|
||||
for inst in insts:
|
||||
self.add_inst(inst)
|
||||
else:
|
||||
insts.ports = self.check(insts.ports)
|
||||
self.insts.append(insts)
|
||||
|
||||
def build_nets(self):
|
||||
for dev in self.devs:
|
||||
for port in dev.ports():
|
||||
if port not in self.ports and port not in self.nets and port not in self.sources:
|
||||
self.nets.append(port)
|
||||
|
||||
def build_AND(self):
|
||||
nets = self.nets + [x for x in self.ports if x not in self.sources]
|
||||
for net in nets:
|
||||
source = list()
|
||||
drain = list()
|
||||
for dev in self.devs:
|
||||
if dev.source == net:
|
||||
source.append(dev)
|
||||
if dev.drain == net:
|
||||
drain.append(dev)
|
||||
|
||||
lst = list()
|
||||
if len(source) == 1 and len(drain) == 1 and source[0].bulk == drain[0].bulk and source[0].kind == drain[0].kind:
|
||||
lst = drain+source
|
||||
elif len(source) == 2 and len(drain) == 0 and source[0].bulk == source[1].bulk and source[0].kind == source[1].kind:
|
||||
source.sort(key=lambda dev: dev.drain not in [self.vdd, self.gnd])
|
||||
source[0].flip()
|
||||
lst = source
|
||||
elif len(source) == 0 and len(drain) == 2 and drain[0].bulk == drain[1].bulk and drain[0].kind == drain[1].kind:
|
||||
drain.sort(key=lambda dev: dev.source not in [self.vdd, self.gnd])
|
||||
drain[1].flip()
|
||||
lst = drain
|
||||
|
||||
if lst:
|
||||
self.devs.remove(lst[0])
|
||||
self.devs.remove(lst[1])
|
||||
self.nets.remove(net)
|
||||
devs = list()
|
||||
|
||||
if isinstance(lst[0], Expr) and lst[0].op == '&':
|
||||
devs += lst[0].devs
|
||||
else:
|
||||
devs.append(lst[0])
|
||||
|
||||
if isinstance(lst[1], Expr) and lst[1].op == '&':
|
||||
devs += lst[1].devs
|
||||
else:
|
||||
devs.append(lst[1])
|
||||
|
||||
self.devs.append(Expr('&', lst[0].kind, lst[0].source, lst[1].drain, lst[0].bulk, devs))
|
||||
|
||||
def build_OR(self):
|
||||
nets = self.nets + [x for x in self.ports if x not in self.sources] + self.sources
|
||||
for source in nets:
|
||||
for drain in nets:
|
||||
devs = dict()
|
||||
for dev in self.devs:
|
||||
if can_merge(dev.source, source, [self.vdd, self.gnd]) and dev.drain == drain:
|
||||
if dev.bulk not in devs:
|
||||
devs[dev.bulk] = [[dev], [], dev.kind]
|
||||
else:
|
||||
devs[dev.bulk][0].append(dev)
|
||||
elif can_merge(dev.drain, source, [self.vdd, self.gnd]) and dev.source == drain:
|
||||
if dev.bulk not in devs:
|
||||
devs[dev.bulk] = [[], [dev], dev.kind]
|
||||
else:
|
||||
devs[dev.bulk][1].append(dev)
|
||||
|
||||
for bulk,dev in devs.items():
|
||||
if len(dev[0]) + len(dev[1]) > 1:
|
||||
for d in dev[0]:
|
||||
self.devs.remove(d)
|
||||
for d in dev[1]:
|
||||
self.devs.remove(d)
|
||||
d.flip()
|
||||
|
||||
self.devs.append(Expr('|', dev[2], source, drain, bulk, dev[0] + dev[1]))
|
||||
|
||||
def build_shared(self):
|
||||
nets = self.nets + [x for x in self.ports if x not in self.sources]
|
||||
for net in nets:
|
||||
source = list()
|
||||
drain = list()
|
||||
gate = list()
|
||||
for dev in self.devs:
|
||||
if dev.source == net:
|
||||
source.append(dev)
|
||||
if dev.drain == net:
|
||||
drain.append(dev)
|
||||
if net in dev.gates():
|
||||
gate.append(dev)
|
||||
|
||||
if not gate and len(source) > 1 and len(drain) == 1 and source[0].bulk == drain[0].bulk and source[0].kind == drain[0].kind:
|
||||
if net in self.nets:
|
||||
self.nets.remove(net)
|
||||
for dev in source:
|
||||
dev.source = '@' + dev.source
|
||||
dev.shared_source = source
|
||||
for dev in drain:
|
||||
dev.drain = '@' + dev.drain
|
||||
dev.shared_drain = drain
|
||||
|
||||
def build_exprs(self):
|
||||
done_shared = False
|
||||
while not done_shared:
|
||||
done_shared = True
|
||||
|
||||
l = len(self.devs)+1
|
||||
while len(self.devs) < l:
|
||||
l = len(self.devs)
|
||||
self.build_AND()
|
||||
self.build_OR()
|
||||
if len(self.devs) < l:
|
||||
done_shared = False
|
||||
|
||||
self.build_shared()
|
||||
|
||||
self.devs.sort(key=lambda dev: [dev.drain, dev.kind])
|
||||
|
||||
def emit(self, tab = ''):
|
||||
result = [tab + 'export defproc {name}({glob}bool {ports}) {{'.format(
|
||||
name = self.name,
|
||||
glob = "globals g; " if self.use_globals else "",
|
||||
ports = ', '.join(self.ports))]
|
||||
|
||||
if self.nets:
|
||||
result += [tab + '\tbool ' + ', '.join(self.nets) + ';\n']
|
||||
|
||||
for inst in self.insts:
|
||||
result.append(inst.emit(tab + '\t'))
|
||||
|
||||
result.append('')
|
||||
result.append(tab + '\tprs <{vdd}, {gnd} | {vdds}, {gnds}> {{'.format(
|
||||
vdd = self.vdd,
|
||||
gnd = self.gnd,
|
||||
vdds = self.vdds,
|
||||
gnds = self.gnds))
|
||||
|
||||
if self.devs:
|
||||
for dev in self.devs:
|
||||
devstr = dev.emit([self.vdd, self.gnd], tab + '\t\t')
|
||||
if devstr.strip():
|
||||
result.append(devstr)
|
||||
|
||||
result.append(tab + '\t}')
|
||||
result.append(tab + '}\n')
|
||||
return result
|
||||
|
||||
def interpret_length(length):
|
||||
if length[-1] == 'm':
|
||||
return float(length[0:-1])*1e-3
|
||||
elif length[-1] == 'u':
|
||||
return float(length[0:-1])*1e-6
|
||||
elif length[-1] == 'n':
|
||||
return float(length[0:-1])*1e-9
|
||||
elif length[-1] == 'p':
|
||||
return float(length[0:-1])*1e-12
|
||||
elif length[-1] == 'f':
|
||||
return float(length[0:-1])*1e-15
|
||||
return float(length)
|
||||
|
||||
def print_help():
|
||||
print("Usage: spi2act.py [options] <lambda> <spice file>")
|
||||
print("\t--globals,-g\tbundles the power rails into a globals structure.")
|
||||
print("")
|
||||
print("Tech Lambda")
|
||||
print("ibm10lp 0.025e-6")
|
||||
print("ibm12soi 0.019e-6")
|
||||
print("ibm28lp 0.015e-6")
|
||||
print("ibm9lp 0.05e-6")
|
||||
print("ibm9sf 0.04e-6")
|
||||
print("sam28lp 0.015e-6")
|
||||
print("st28soi 0.015e-6")
|
||||
print("tsmc65 0.03e-6")
|
||||
print("xlp2 0.075e-6")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) <= 2 or sys.argv[1] == '--help' or sys.argv[1] == '-h':
|
||||
print_help()
|
||||
else:
|
||||
myproc = None
|
||||
scale = None
|
||||
in_path = None
|
||||
use_globals = False
|
||||
swap_source_drain = False
|
||||
for arg in sys.argv[1:]:
|
||||
if arg[0] == '-':
|
||||
if arg == '--globals' or arg == '-g':
|
||||
use_globals = True
|
||||
elif arg == '--swap' or arg == '-s':
|
||||
swap_source_drain = True
|
||||
else:
|
||||
print("error: unrecognized option '{arg}'".format(arg=arg))
|
||||
print("")
|
||||
print_help()
|
||||
sys.exit()
|
||||
elif not scale:
|
||||
scale = float(arg)
|
||||
elif not in_path:
|
||||
in_path = arg
|
||||
|
||||
if use_globals:
|
||||
print("import \"globals.act\";")
|
||||
print("")
|
||||
|
||||
with open(in_path, "r") as lines:
|
||||
for number, line in enumerate(lines):
|
||||
line = line.strip()
|
||||
if line:
|
||||
if '.subckt' in line.lower():
|
||||
subckt = line.split(' ')
|
||||
myproc = Process(name = subckt[1], ports = subckt[2:], use_globals = use_globals)
|
||||
elif '.ends' in line.lower():
|
||||
if myproc:
|
||||
myproc.build_nets()
|
||||
myproc.build_exprs()
|
||||
print('\n'.join(myproc.emit()))
|
||||
myproc = None
|
||||
else:
|
||||
print("error: dangling '.ends' on line " + str(number))
|
||||
sys.exit()
|
||||
elif line[0].lower() != '*':
|
||||
# this is a device definition
|
||||
line = line.replace(" =", "=")
|
||||
line = line.replace("= ", "=")
|
||||
devs = line.split(' ')
|
||||
attrs = dict()
|
||||
ports = list()
|
||||
instname = None
|
||||
typename = None
|
||||
for dev in devs:
|
||||
if '=' in dev:
|
||||
attr = dev.split('=')
|
||||
attrs[attr[0]] = attr[1]
|
||||
else:
|
||||
port = dev.split(':')
|
||||
ports.append(port[0])
|
||||
instname = ports[0]
|
||||
ports = ports[1:]
|
||||
|
||||
if line[0].lower() == 'x':
|
||||
# this device is a subckt instantiation
|
||||
typename = ports[-1]
|
||||
ports = ports[:-1]
|
||||
|
||||
if myproc and 'fet' in typename:
|
||||
# this subckt is a mosfet
|
||||
myproc.add_devs(Transistor(
|
||||
inst = instname,
|
||||
kind = 'p' if 'pfet' in typename else 'n',
|
||||
# .subckt sky130_fd_pr__nfet_01v8 d g s b
|
||||
gate = ports[1],
|
||||
source = ports[2] if not swap_source_drain else ports[0],
|
||||
drain = ports[0] if not swap_source_drain else ports[2],
|
||||
bulk = ports[3],
|
||||
width = interpret_length(attrs['w'])/scale,
|
||||
length = interpret_length(attrs['l'])/scale))
|
||||
elif myproc and 'diode' not in typename:
|
||||
myproc.add_insts(Instance(
|
||||
name = instname,
|
||||
typename = typename,
|
||||
ports = ports))
|
||||
elif line[0].lower() == 'm':
|
||||
# this device is a transistor instantiation
|
||||
typename = ports[-1]
|
||||
ports = ports[:-1]
|
||||
if 'w' not in attrs or 'l' not in attrs:
|
||||
print(line)
|
||||
|
||||
# this subckt is a mosfet
|
||||
myproc.add_devs(Transistor(
|
||||
inst = instname,
|
||||
kind = 'p' if 'pfet' in typename else 'n',
|
||||
gate = ports[1],
|
||||
source = ports[2] if not swap_source_drain else ports[0],
|
||||
drain = ports[0] if not swap_source_drain else ports[2],
|
||||
bulk = ports[3],
|
||||
width = interpret_length(attrs['w'])/scale,
|
||||
length = interpret_length(attrs['l'])/scale))
|
||||
|
Loading…
Reference in New Issue
Block a user