def file parser
This commit is contained in:
1 parent
afb0a64953
commit
153442a10a
1 file changed
+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))
|
||||||
Reference in new issue
Block a user