Stefan Holst
1 year ago
1 changed files with 285 additions and 0 deletions
@ -0,0 +1,285 @@
@@ -0,0 +1,285 @@
|
||||
from collections import defaultdict |
||||
|
||||
from lark import Lark, Transformer, Tree |
||||
|
||||
from kyupy import readtext |
||||
|
||||
|
||||
class DefNet: |
||||
def __init__(self, name): |
||||
self.name = name |
||||
self.pins = [] |
||||
|
||||
@property |
||||
def wires(self): |
||||
ww = defaultdict(list) |
||||
[ww[dw.layer].append((int(dw.width), dw.wire_points)) for dw in self.routed if len(dw.wire_points) > 0] |
||||
return ww |
||||
|
||||
@property |
||||
def vias(self): |
||||
vv = defaultdict(list) |
||||
[vv[vtype].extend(locs) for dw in self.routed for vtype, locs in dw.vias.items()] |
||||
return vv |
||||
|
||||
|
||||
class DefWire: |
||||
def __init__(self): |
||||
self.layer = None |
||||
self.width = None |
||||
self.points = [] |
||||
|
||||
@property |
||||
def wire_points(self): |
||||
start = [self.points[0]] |
||||
rest = [p for p in self.points[1:] if not isinstance(p[0], str)] # skip over vias |
||||
return start + rest if len(rest) > 0 else [] |
||||
|
||||
@property |
||||
def vias(self): |
||||
vv = defaultdict(list) |
||||
loc = self.points[0] |
||||
for p in self.points[1:]: |
||||
if not isinstance(p[0], str): # new location |
||||
loc = (loc[0] if p[0] is None else p[0], loc[1] if p[1] is None else p[1]) # if None, keep previous value |
||||
continue |
||||
vtype, param = p |
||||
if isinstance(param, tuple): # expand "DO x BY y STEP xs ys" |
||||
x_cnt, y_cnt, x_sp, y_sp = param |
||||
[vv[vtype].append((loc[0] + x*x_sp, loc[1] + y*y_sp, 'N')) for x in range(x_cnt) for y in range(y_cnt)] |
||||
else: |
||||
vv[vtype].append((loc[0], loc[1], param or 'N')) |
||||
return vv |
||||
|
||||
def __repr__(self): |
||||
return f'<DefWire {self.layer} {self.width} {self.points}>' |
||||
|
||||
|
||||
class DefVia: |
||||
def __init__(self, name): |
||||
self.name = name |
||||
self.rowcol = [1, 1] |
||||
self.cutspacing = [0, 0] |
||||
|
||||
|
||||
class DefPin: |
||||
def __init__(self, name): |
||||
self.name = name |
||||
self.points = [] |
||||
|
||||
|
||||
class DefFile: |
||||
def __init__(self): |
||||
self.rows = [] |
||||
self.tracks = [] |
||||
self.units = [] |
||||
self.vias = {} |
||||
self.components = {} |
||||
self.pins = {} |
||||
self.specialnets = {} |
||||
self.nets = {} |
||||
|
||||
|
||||
class DefTransformer(Transformer): |
||||
def __init__(self): self.def_file = DefFile() |
||||
def start(self, args): return self.def_file |
||||
def design(self, args): self.def_file.design = args[0].value |
||||
def point(self, args): return tuple(int(arg.value) if arg != '*' else None for arg in args) |
||||
def do_step(self, args): return tuple(map(int, args)) |
||||
def spnet_wires(self, args): return args[0].lower(), args[1:] |
||||
def net_wires(self, args): return args[0].lower(), args[1:] |
||||
def sppoints(self, args): return args |
||||
def points(self, args): return args |
||||
def net_pin(self, args): return '__pin__', (args[0].value, args[1].value) |
||||
def net_opt(self, args): return args[0].lower(), args[1].value |
||||
|
||||
def file_stmt(self, args): |
||||
value = args[1].value |
||||
value = value[1:-1] if value[0] == '"' else value |
||||
setattr(self.def_file, args[0].lower(), value) |
||||
|
||||
def design_stmt(self, args): |
||||
stmt = args[0].lower() |
||||
if stmt == 'units': self.def_file.units.append((args[1].value, args[2].value, int(args[3]))) |
||||
elif stmt == 'diearea': self.def_file.diearea = args[1:] |
||||
elif stmt == 'row': |
||||
self.def_file.rows.append((args[1].value, # rowName |
||||
args[2].value, # siteName |
||||
(int(args[3]), int(args[4])), # origin x/y |
||||
args[5].value, # orientation |
||||
max(args[6][0], args[6][1]), # number of sites |
||||
max(args[6][2], args[6][3]) # site width |
||||
)) |
||||
elif stmt == 'tracks': |
||||
self.def_file.tracks.append((args[1].value, # orientation |
||||
int(args[2]), # start |
||||
int(args[3]), # number of tracks |
||||
int(args[4]), # spacing |
||||
args[5].value # layer |
||||
)) |
||||
|
||||
def vias_stmt(self, args): |
||||
via = DefVia(args[0].value) |
||||
[setattr(via, opt, val) for opt, val in args[1:]] |
||||
self.def_file.vias[via.name] = via |
||||
|
||||
def vias_opt(self, args): |
||||
opt = args[0].lower() |
||||
if opt in ['viarule', 'pattern']: val = args[1].value |
||||
elif opt in ['layers']: val = [arg.value for arg in args[1:]] |
||||
else: val = [int(arg) for arg in args[1:]] |
||||
return opt, val |
||||
|
||||
def comp_stmt(self, args): |
||||
name = args[0].value |
||||
kind = args[1].value |
||||
point = args[2] |
||||
orientation = args[3].value |
||||
self.def_file.components[name] = (kind, point, orientation) |
||||
|
||||
def pins_stmt(self, args): |
||||
pin = DefPin(args[0].value) |
||||
[pin.points.append(val) if opt == 'placed' else setattr(pin, opt, val) for opt, val in args[1:]] |
||||
self.def_file.pins[pin.name] = pin |
||||
|
||||
def pins_opt(self, args): |
||||
opt = args[0].lower() |
||||
if opt in ['net', 'direction', 'use']: val = args[1].value |
||||
elif opt in ['layer']: val = [args[1].value] + args[2:] |
||||
elif opt in ['placed']: val = (args[1][0], args[1][1], args[2].value) |
||||
else: val = [] |
||||
return opt, val |
||||
|
||||
def spnets_stmt(self, args): |
||||
dnet = DefNet(args[0].value) |
||||
for arg in args[1:]: |
||||
if arg[0] == '__pin__': dnet.pins.append(arg[1]) |
||||
else: setattr(dnet, arg[0], arg[1]) |
||||
self.def_file.specialnets[dnet.name] = dnet |
||||
|
||||
def nets_stmt(self, args): |
||||
dnet = DefNet(args[0].value) |
||||
for arg in args[1:]: |
||||
if arg[0] == '__pin__': dnet.pins.append(arg[1]) |
||||
else: setattr(dnet, arg[0], arg[1]) |
||||
self.def_file.nets[dnet.name] = dnet |
||||
|
||||
def spwire(self, args): |
||||
wire = DefWire() |
||||
wire.layer = args[0].value |
||||
wire.width = args[1].value |
||||
wire.points = args[-1] |
||||
return wire |
||||
|
||||
def wire(self, args): |
||||
wire = DefWire() |
||||
wire.layer = args[0].value |
||||
wire.points = args[-1] |
||||
return wire |
||||
|
||||
def sppoints_via(self, args): |
||||
if len(args) == 1: return args[0].value, None |
||||
else: return args[0].value, args[1] |
||||
|
||||
def points_via(self, args): |
||||
if len(args) == 1: return args[0].value, 'N' |
||||
else: return args[0].value, args[1].value.strip() |
||||
|
||||
|
||||
GRAMMAR = r""" |
||||
start: /#[^\n]*/? file_stmt* |
||||
|
||||
?file_stmt: /VERSION/ ID ";" |
||||
| /DIVIDERCHAR/ STRING ";" |
||||
| /BUSBITCHARS/ STRING ";" |
||||
| design |
||||
|
||||
design: "DESIGN" ID ";" design_stmt* "END" "DESIGN" |
||||
|
||||
?design_stmt: /UNITS/ ID ID NUMBER ";" |
||||
| /DIEAREA/ point+ ";" |
||||
| /ROW/ ID ID NUMBER NUMBER ID do_step ";" |
||||
| /TRACKS/ /[XY]/ NUMBER "DO" NUMBER "STEP" NUMBER "LAYER" ID ";" |
||||
| propdef | vias | nondef | comp | pins | pinprop | spnets | nets |
||||
|
||||
propdef: "PROPERTYDEFINITIONS" propdef_stmt* "END" "PROPERTYDEFINITIONS" |
||||
propdef_stmt: /COMPONENTPIN/ ID ID ";" |
||||
|
||||
vias: "VIAS" NUMBER ";" vias_stmt* "END" "VIAS" |
||||
vias_stmt: "-" ID vias_opt* ";" |
||||
vias_opt: "+" /VIARULE/ ID |
||||
| "+" /CUTSIZE/ NUMBER NUMBER |
||||
| "+" /LAYERS/ ID ID ID |
||||
| "+" /CUTSPACING/ NUMBER NUMBER |
||||
| "+" /ENCLOSURE/ NUMBER NUMBER NUMBER NUMBER |
||||
| "+" /ROWCOL/ NUMBER NUMBER |
||||
| "+" /PATTERN/ ID |
||||
|
||||
nondef: "NONDEFAULTRULES" NUMBER ";" nondef_stmt+ "END" "NONDEFAULTRULES" |
||||
nondef_stmt: "-" ID ( "+" /HARDSPACING/ |
||||
| "+" /LAYER/ ID "WIDTH" NUMBER "SPACING" NUMBER |
||||
| "+" /VIA/ ID )* ";" |
||||
|
||||
comp: "COMPONENTS" NUMBER ";" comp_stmt* "END" "COMPONENTS" |
||||
comp_stmt: "-" ID ID "+" "PLACED" point ID ";" |
||||
|
||||
pins: "PINS" NUMBER ";" pins_stmt* "END" "PINS" |
||||
pins_stmt: "-" ID pins_opt* ";" |
||||
pins_opt: "+" /NET/ ID |
||||
| "+" /SPECIAL/ |
||||
| "+" /DIRECTION/ ID |
||||
| "+" /USE/ ID |
||||
| "+" /PORT/ |
||||
| "+" /LAYER/ ID point point |
||||
| "+" /PLACED/ point ID |
||||
|
||||
pinprop: "PINPROPERTIES" NUMBER ";" pinprop_stmt* "END" "PINPROPERTIES" |
||||
pinprop_stmt: "-" "PIN" ID "+" "PROPERTY" ID STRING ";" |
||||
|
||||
spnets: "SPECIALNETS" NUMBER ";" spnets_stmt* "END" "SPECIALNETS" |
||||
spnets_stmt: "-" ID ( net_pin | net_opt | spnet_wires )* ";" |
||||
|
||||
spnet_wires: "+" ( /COVER/ | /FIXED/ | /ROUTED/ ) spwire ( "NEW" spwire )* |
||||
|
||||
spwire: ID NUMBER spwire_opt* sppoints |
||||
spwire_opt: "+" /SHAPE/ ID |
||||
| "+" /STYLE/ ID |
||||
|
||||
sppoints: point ( point | sppoints_via )+ |
||||
sppoints_via: ID do_step? |
||||
|
||||
nets: "NETS" NUMBER ";" nets_stmt* "END" "NETS" |
||||
nets_stmt: "-" ID ( net_pin | net_opt | net_wires )* ";" |
||||
|
||||
net_pin: "(" ID ID ")" |
||||
net_opt: "+" /USE/ ID |
||||
| "+" /NONDEFAULTRULE/ ID |
||||
net_wires: "+" ( /COVER/ | /FIXED/ | /ROUTED/ | /NOSHIELD/ ) wire ( "NEW" wire )* |
||||
|
||||
wire: ID wire_opt points |
||||
wire_opt: ( "TAPER" | "TAPERRULE" ID )? ("STYLE" ID)? |
||||
|
||||
points: point ( point | points_via )+ |
||||
points_via: ID ORIENTATION? |
||||
|
||||
point: "(" (NUMBER|/\*/) (NUMBER|/\*/) NUMBER? ")" |
||||
|
||||
do_step: "DO" NUMBER "BY" NUMBER "STEP" (NUMBER|SIGNED_NUMBER) (NUMBER|SIGNED_NUMBER) |
||||
|
||||
ORIENTATION.2: /F?[NWES]/ WS |
||||
ID: /[^ \t\f\r\n+][^ \t\f\r\n]*/ |
||||
STRING : "\"" /.*?/s /(?<!\\)(\\\\)*?/ "\"" |
||||
WS: /[ \t\f\r\n]/ |
||||
|
||||
%import common.NUMBER |
||||
%import common.SIGNED_NUMBER |
||||
%ignore WS (/#[^\n]*/)? |
||||
""" |
||||
|
||||
|
||||
def parse(text): |
||||
return Lark(GRAMMAR, parser="lalr", transformer=DefTransformer()).parse(text) |
||||
|
||||
|
||||
def load(file): |
||||
return parse(readtext(file)) |
Loading…
Reference in new issue