#!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))