Stefan Holst
1 year ago
1 changed files with 285 additions and 0 deletions
@ -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