| """ |
| Utilities for SDF file parsing to determine cell timings |
| """ |
| import sys |
| |
| |
| class SDFData: |
| def __init__(self): |
| self.cells = {} |
| |
| class Delay: |
| def __init__(self, minv, typv, maxv): |
| self.minv = minv |
| self.typv = typv |
| self.maxv = maxv |
| |
| |
| class IOPath: |
| def __init__(self, from_pin, to_pin, rising, falling): |
| self.from_pin = from_pin |
| self.to_pin = to_pin |
| self.rising = rising |
| self.falling = falling |
| |
| |
| class SetupHoldCheck: |
| def __init__(self, pin, clock, setup, hold): |
| self.pin = pin |
| self.clock = clock |
| self.setup = setup |
| self.hold = hold |
| |
| |
| class WidthCheck: |
| def __init__(self, clock, width): |
| self.clock = clock |
| self.width = width |
| |
| |
| class Interconnect: |
| def __init__(self, from_net, to_net, rising, falling): |
| self.from_net = from_net |
| self.to_net = to_net |
| self.rising = rising |
| self.falling = falling |
| |
| |
| class CellData: |
| def __init__(self, celltype, inst): |
| self.type = celltype |
| self.inst = inst |
| self.entries = [] |
| self.interconnect = {} |
| |
| |
| def parse_sexpr(stream): |
| content = [] |
| buffer = "" |
| instr = False |
| while True: |
| c = stream.read(1) |
| assert c != "", "unexpected end of file" |
| if instr: |
| if c == '"': |
| instr = False |
| else: |
| buffer += c |
| else: |
| if c == '(': |
| content.append(parse_sexpr(stream)) |
| elif c == ')': |
| if buffer != "": |
| content.append(buffer) |
| return content |
| elif c.isspace(): |
| if buffer != "": |
| content.append(buffer) |
| buffer = "" |
| elif c == '"': |
| instr = True |
| else: |
| buffer += c |
| |
| |
| def parse_sexpr_file(filename): |
| with open(filename, 'r') as f: |
| assert f.read(1) == '(' |
| return parse_sexpr(f) |
| |
| |
| def parse_delay(delay): |
| sp = [int(x) for x in delay.split(":")] |
| assert len(sp) == 3 |
| return Delay(sp[0], sp[1], sp[2]) |
| |
| |
| def parse_sdf_file(filename): |
| sdata = parse_sexpr_file(filename) |
| assert sdata[0] == "DELAYFILE" |
| sdf = SDFData() |
| for entry in sdata[1:]: |
| if entry[0] != "CELL": |
| continue |
| assert entry[1][0] == "CELLTYPE" |
| celltype = entry[1][1] |
| assert entry[2][0] == "INSTANCE" |
| if len(entry[2]) > 1: |
| inst = entry[2][1] |
| else: |
| inst = "top" |
| cell = CellData(celltype, inst) |
| for subentry in entry[3:]: |
| if subentry[0] == "DELAY": |
| assert subentry[1][0] == "ABSOLUTE" |
| for delay in subentry[1][1:]: |
| if delay[0] == "IOPATH": |
| cell.entries.append( |
| IOPath(delay[1], delay[2], parse_delay(delay[3][0]), parse_delay(delay[4][0]))) |
| elif delay[0] == "INTERCONNECT": |
| cell.interconnect[(delay[1], delay[2])] = Interconnect(delay[1], delay[2], |
| parse_delay(delay[3][0]), |
| parse_delay(delay[4][0])) |
| elif subentry[0] == "TIMINGCHECK": |
| for check in subentry[1:]: |
| if check[0] == "SETUPHOLD": |
| cell.entries.append( |
| SetupHoldCheck(check[1], check[2], parse_delay(check[3][0]), parse_delay(check[4][0]))) |
| elif check[0] == "WIDTH": |
| cell.entries.append(WidthCheck(check[1], parse_delay(check[2][0]))) |
| sdf.cells[inst] = cell |
| return sdf |