This commit is contained in:
PhillipRambo 2024-12-17 11:54:02 +01:00
parent 6fbf7a0381
commit 3abd3c5a9c
2 changed files with 379 additions and 0 deletions

View File

@ -0,0 +1,379 @@
"""Module to automatically generate a gallery of devices out of spice netlist creating a new GDS file. Can be used in
Klayout's batch mode. For example:
klayout -n sg13g2 -zz -r generator.py -rd netlist=netlist.spice -rd output=macros/gallery.gds
"""
import pathlib
import sys
import re
import argparse
from typing import List, Dict
import pya
import klayout.db
LIB = 'SG13_dev'
def parse_netlist(netlist: str) -> List[Dict[str, str]]:
# Define regular expression patterns for each component type
patterns = {
'ipin': re.compile(r"^\*\.ipin\s+(\S+)\s*$", re.MULTILINE),
'opin': re.compile(r"^\*\.opin\s+(\S+)\s*$", re.MULTILINE),
'iopin': re.compile(r"^\*\.iopin\s+(\S+)\s*$", re.MULTILINE),
'bjt': re.compile(r"^XQ(\S+)\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)\s+Nx=([\d\.]+)(?:\s+le=([\d\.e-]+))?\s*$", re.MULTILINE),
'capacitor': re.compile(r"^XC(\S+)\s+\S+\s+\S+\s+(\S+)\s+w=([\d\.e-]+)\s+l=([\d\.e-]+)\s+m=([\d\.]+)\s*$", re.MULTILINE),
'diode': re.compile(r"^XD(\S+)\s+\S+\s+\S+\s+(\S+)\s+l=([\d\.a-zA-Z]+)\s+w=([\d\.a-zA-Z]+)\s*$", re.MULTILINE),
'resistor': re.compile(r"^XR(\S+)\s+\S+\s+\S+\s+(\S+)\s+w=([\d\.e-]+)\s+l=([\d\.e-]+)\s+m=([\d\.]+)\s+b=([\d\.]+)\s*$", re.MULTILINE),
'mosfet': re.compile(r"^XM(\S+)\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)\s+w=([\d\.u-]+)\s+l=([\d\.u-]+)\s+ng=([\d\.]+)\s+m=([\d\.]+)(?:\s+rfmode=([\d\.]+))?\s*$", re.MULTILINE),
'tap': re.compile(r"^XR(\S+)\s+\S+\s+\S+\s+(\S+)\s*$", re.MULTILINE)
}
components = []
# Parse pins
for pin_type in ['ipin', 'opin', 'iopin']:
for match in patterns[pin_type].finditer(netlist):
components.append({
'name': match.group(1),
'model': pin_type
})
# Parse BJTs
for match in patterns['bjt'].finditer(netlist):
components.append({
'name': f"XQ{match.group(1)}",
'model': match.group(2),
'Nx': match.group(3),
'le': match.group(4) if match.group(4) else None
})
# Parse capacitors
for match in patterns['capacitor'].finditer(netlist):
components.append({
'name': f"XC{match.group(1)}",
'model': match.group(2),
'w': match.group(3),
'l': match.group(4),
'm': match.group(5)
})
# Parse diodes
for match in patterns['diode'].finditer(netlist):
components.append({
'name': f"XD{match.group(1)}",
'model': match.group(2),
'l': match.group(3),
'w': match.group(4)
})
# Parse resistors
for match in patterns['resistor'].finditer(netlist):
components.append({
'name': f"XR{match.group(1)}",
'model': match.group(2),
'w': match.group(3),
'l': match.group(4),
'm': match.group(5),
'b': match.group(6)
})
# Parse MOSFETs
for match in patterns['mosfet'].finditer(netlist):
components.append({
'name': f"XM{match.group(1)}",
'model': match.group(2),
'w': match.group(3),
'l': match.group(4),
'ng': match.group(5),
'm': match.group(6),
'rfmode': match.group(7) if match.group(7) else None
})
#Parse ntap
for match in patterns['tap'].finditer(netlist):
components.append({
'name': f"XR{match.group(1)}",
'model': match.group(2) # In this case, group 4 is the ntap model
})
return components
def generate_devices(netlist: str, output: str):
components = parse_netlist(netlist)
lib = pya.Library.library_by_name(LIB)
layout = klayout.db.Layout(True)
layout.dbu = 0.001
top_cell = layout.cell(layout.add_cell('gallery'))
res = find_instances_by_model(components, 'rppd')
xstep = 0
cell_w = 0
y_offset = 100
hmax = 0
for item in res:
width = float(item['w'])*1e6
length = float(item['l'])*1e6
bends = int(item['b'])
m = int(item['m'])
for i in range(1,m+1):
pcell_decl = lib.layout().pcell_declaration('rppd')
params = pcell_decl.params_as_hash(pcell_decl.get_parameters())
pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'b' : f'{bends}'})
cell=layout.cell(pcell)
bbox=cell.bbox()
xstep = xstep + 100 + cell_w
top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep,y_offset ))))
cell_w = bbox.width()
cell_h = bbox.height()
if cell_h > hmax:
hmax = cell_h
# print(f'w: {cell_w}, h: {cell_h}, xstep: {xstep}')
res = find_instances_by_model(components, 'rhigh')
xstep = 0
cell_w = 0
y_offset = y_offset + hmax
hmax = 0
for item in res:
width = float(item['w'])*1e6
length = float(item['l'])*1e6
bends = int(item['b'])
m = int(item['m'])
for i in range(1,m+1):
pcell_decl = lib.layout().pcell_declaration('rhigh')
params = pcell_decl.params_as_hash(pcell_decl.get_parameters())
pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'b' : f'{bends}'})
cell=layout.cell(pcell)
bbox=cell.bbox()
xstep = xstep + 100 + cell_w
top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset))))
cell_w=bbox.width()
cell_h=bbox.height()
if cell_h > hmax:
hmax = cell_h
res = find_instances_by_model(components, 'rsil')
xstep = 0
cell_w = 0
y_offset = y_offset + hmax
hmax = 0
for item in res:
width = float(item['w'])*1e6
length = float(item['l'])*1e6
bends = int(item['b'])
m = int(item['m'])
for i in range(1,m+1):
pcell_decl = lib.layout().pcell_declaration('rsil')
params = pcell_decl.params_as_hash(pcell_decl.get_parameters())
pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'b' : f'{bends}'})
cell=layout.cell(pcell)
bbox=cell.bbox()
xstep = xstep + 100 + cell_w
top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep,y_offset))))
cell_w=bbox.width()
cell_h=bbox.height()
if cell_h > hmax:
hmax = cell_h
mos = find_instances_by_model(components, 'sg13_lv_nmos')
xstep = 0
cell_w = 0
y_offset = y_offset + hmax
hmax = 0
for item in mos:
width = float(item['w'][:-1])
length = float(item['l'][:-1])
ng = int(item['ng'])
m = int(item['m'])
for i in range(1,m+1):
pcell_decl = lib.layout().pcell_declaration('nmos')
params = pcell_decl.params_as_hash(pcell_decl.get_parameters())
pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'ng' : f'{ng}'})
cell=layout.cell(pcell)
bbox=cell.bbox()
xstep = xstep + 100 + cell_w
top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset))))
cell_w=bbox.width()
cell_h=bbox.height()
if cell_h > hmax:
hmax = cell_h
mos = find_instances_by_model(components, 'sg13_hv_nmos')
xstep = 0
cell_w = 0
y_offset = y_offset + hmax
hmax = 0
for item in mos:
width = float(item['w'][:-1])
length = float(item['l'][:-1])
ng = int(item['ng'])
m = int(item['m'])
for i in range(1,m+1):
pcell_decl = lib.layout().pcell_declaration('nmosHV')
params = pcell_decl.params_as_hash(pcell_decl.get_parameters())
pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'ng' : f'{ng}'})
cell=layout.cell(pcell)
bbox=cell.bbox()
xstep = xstep + 100 + cell_w
top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset))))
cell_w=bbox.width()
cell_h=bbox.height()
if cell_h > hmax:
hmax = cell_h
mos = find_instances_by_model(components, 'sg13_lv_pmos')
xstep = 0
cell_w = 0
y_offset = y_offset + hmax
hmax = 0
for item in mos:
name = item['name']
width = float(item['w'][:-1])
length = float(item['l'][:-1])
ng = int(item['ng'])
m = int(item['m'])
for i in range(1,m+1):
pcell_decl = lib.layout().pcell_declaration('pmos')
params = pcell_decl.params_as_hash(pcell_decl.get_parameters())
pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'ng' : f'{ng}'})
cell=layout.cell(pcell)
bbox=cell.bbox()
xstep = xstep + 100 + cell_w
top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset))))
cell_w=bbox.width()
cell_h=bbox.height()
if cell_h > hmax:
hmax = cell_h
mos = find_instances_by_model(components, 'sg13_hv_pmos')
xstep = 0
cell_w = 0
y_offset = y_offset + hmax
hmax = 0
for item in mos:
name = item['name']
width = float(item['w'][:-1])
length = float(item['l'][:-1])
ng = int(item['ng'])
m = int(item['m'])
for i in range(1,m+1):
pcell_decl = lib.layout().pcell_declaration('pmosHV')
params = pcell_decl.params_as_hash(pcell_decl.get_parameters())
pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u', 'ng' : f'{ng}'})
cell=layout.cell(pcell)
bbox=cell.bbox()
xstep = xstep + 100 + cell_w
top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset))))
cell_w=bbox.width()
cell_h=bbox.height()
if cell_h > hmax:
hmax = cell_h
cmim = find_instances_by_model(components, 'cap_cmim')
xstep = 0
cell_w = 0
y_offset = y_offset + hmax
hmax = 0
for item in cmim:
width = float(item['w'])*1e6
length = float(item['l'])*1e6
m = int(item['m'])
for i in range(1,m+1):
pcell_decl = lib.layout().pcell_declaration('cmim')
params = pcell_decl.params_as_hash(pcell_decl.get_parameters())
pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u'})
cell=layout.cell(pcell)
bbox=cell.bbox()
xstep = xstep + 100 + cell_w
top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset))))
cell_w=bbox.width()
cell_h=bbox.height()
if cell_h > hmax:
hmax = cell_h
dant = find_instances_by_model(components, 'dantenna')
xstep = 0
cell_w = 0
y_offset = y_offset + hmax
hmax = 0
for item in dant:
name = item['name']
model = item['model']
width = float(item['w'][:-1])
length = float(item['l'][:-1])
pcell_decl = lib.layout().pcell_declaration('dantenna')
params = pcell_decl.params_as_hash(pcell_decl.get_parameters())
pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u'})
cell=layout.cell(pcell)
bbox=cell.bbox()
xstep = xstep + 100 + cell_w
top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset))))
cell_w=bbox.width()
cell_h=bbox.height()
if cell_h > hmax:
hmax = cell_h
dant = find_instances_by_model(components, 'dpantenna')
xstep = 0
cell_w = 0
y_offset = y_offset + hmax
hmax = 0
for item in dant:
name = item['name']
model = item['model']
width = float(item['w'][:-1])
length = float(item['l'][:-1])
pcell_decl = lib.layout().pcell_declaration('dpantenna')
params = pcell_decl.params_as_hash(pcell_decl.get_parameters())
pcell = layout.add_pcell_variant(lib, pcell_decl.id(), {'w': f'{width}u', 'l': f'{length}u'})
cell=layout.cell(pcell)
bbox=cell.bbox()
xstep = xstep + 100 + cell_w
top_cell.insert(klayout.db.CellInstArray(pcell, klayout.db.Trans(klayout.db.Point(xstep, y_offset))))
cell_w=bbox.width()
cell_h=bbox.height()
if cell_h > hmax:
hmax = cell_h
pathlib.Path(output).parent.mkdir(parents=True, exist_ok=True)
layout.write(output)
def find_instances_by_model(components, model_name):
return [item for item in components if item['model'] == model_name]
try:
netlist
except NameError:
print("Missing netlist argument. Please define '-rd netlsit=<path-to-netlist>'")
sys.exit(1)
# sys.exit(1)
try:
output
except NameError:
print("Missing output argument. Please define '-rd output=<path-to-sealring>'")
sys.exit(1)
def main():
with open(netlist, 'r') as file:
netlist_content = file.read()
generate_devices(netlist_content, output) # pylint: disable=undefined-variable
print(f'GDS created, open it using: ->klayout {output}')
if __name__ == "__main__":
main()