| #!ENV_PATH python3 |
| # |
| #--------------------------------------------------------- |
| # LVS failure check |
| # |
| # This is a Python script that parses the comp.json |
| # output from netgen and reports on the number of |
| # errors in the top-level netlist. |
| # |
| #--------------------------------------------------------- |
| # Written by Tim Edwards |
| # efabless, inc. |
| # Pulled from qflow GUI as standalone script Aug 20, 2018 |
| #--------------------------------------------------------- |
| |
| import os |
| import re |
| import sys |
| import json |
| import argparse |
| |
| def count_LVS_failures(filename): |
| with open(filename, 'r') as cfile: |
| lvsdata = json.load(cfile) |
| |
| # Count errors in the JSON file |
| failures = 0 |
| devfail = 0 |
| netfail = 0 |
| pinfail = 0 |
| propfail = 0 |
| netdiff = 0 |
| devdiff = 0 |
| ncells = len(lvsdata) |
| for c in range(0, ncells): |
| cellrec = lvsdata[c] |
| |
| if c == ncells - 1: |
| topcell = True |
| else: |
| topcell = False |
| |
| # Most errors must only be counted for the top cell, because individual |
| # failing cells are flattened and the matching attempted again on the |
| # flattened netlist. |
| |
| if topcell: |
| if 'devices' in cellrec: |
| devices = cellrec['devices'] |
| devlist = [val for pair in zip(devices[0], devices[1]) for val in pair] |
| devpair = list(devlist[p:p + 2] for p in range(0, len(devlist), 2)) |
| for dev in devpair: |
| c1dev = dev[0] |
| c2dev = dev[1] |
| diffdevs = abs(c1dev[1] - c2dev[1]) |
| failures += diffdevs |
| devdiff += diffdevs |
| |
| if 'nets' in cellrec: |
| nets = cellrec['nets'] |
| diffnets = abs(nets[0] - nets[1]) |
| failures += diffnets |
| netdiff += diffnets |
| |
| if 'badnets' in cellrec: |
| badnets = cellrec['badnets'] |
| failures += len(badnets) |
| netfail += len(badnets) |
| |
| if 'badelements' in cellrec: |
| badelements = cellrec['badelements'] |
| failures += len(badelements) |
| devfail += len(badelements) |
| |
| if 'pins' in cellrec: |
| pins = cellrec['pins'] |
| pinlist = [val for pair in zip(pins[0], pins[1]) for val in pair] |
| pinpair = list(pinlist[p:p + 2] for p in range(0, len(pinlist), 2)) |
| for pin in pinpair: |
| # Avoid flagging global vs. local names, e.g., "gnd" vs. "gnd!," |
| # and ignore case when comparing pins. |
| pin0 = re.sub('!$', '', pin[0].lower()) |
| pin1 = re.sub('!$', '', pin[1].lower()) |
| if pin0 != pin1: |
| # The text "(no pin)" indicates a missing pin that can be |
| # ignored because the pin in the other netlist is a no-connect |
| if pin0 != '(no pin)' and pin1 != '(no pin)': |
| failures += 1 |
| pinfail += 1 |
| |
| # Property errors must be counted for every cell |
| if 'properties' in cellrec: |
| properties = cellrec['properties'] |
| failures += len(properties) |
| propfail += len(properties) |
| |
| return [failures, netfail, devfail, pinfail, propfail, netdiff, devdiff] |
| |
| if __name__ == '__main__': |
| |
| parser = argparse.ArgumentParser(description='Parses netgen lvs') |
| parser.add_argument('--file', '-f', required=True) |
| args = parser.parse_args() |
| failures = count_LVS_failures(args.file) |
| |
| total = failures[0] |
| if total > 0: |
| failed = True |
| print('LVS reports:') |
| print(' net count difference = ' + str(failures[5])) |
| print(' device count difference = ' + str(failures[6])) |
| print(' unmatched nets = ' + str(failures[1])) |
| print(' unmatched devices = ' + str(failures[2])) |
| print(' unmatched pins = ' + str(failures[3])) |
| print(' property failures = ' + str(failures[4])) |
| else: |
| print('LVS reports no net, device, pin, or property mismatches.') |
| |
| print('') |
| print('Total errors = ' + str(total)) |
| |