Add netlist comparison script
This commit is contained in:
parent
39b1f0e1ff
commit
27db1c90bc
|
@ -0,0 +1,161 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2019 Jon Evans <jon@craftyjon.com>
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
# This utility compares two netlists generated by Eeschema and reports on
|
||||
# connectivity differences between them. This only is looking at connectivity!
|
||||
# Things like the components section, the visual format of the file, and the
|
||||
# sorting of entries is not compared.
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sexpdata
|
||||
import sys
|
||||
|
||||
|
||||
def extract_nets(sexpr):
|
||||
# Starts with ( export (version N) (...) )
|
||||
for idx, key in enumerate(sexpr):
|
||||
# print("{}: {}".format(idx, key))
|
||||
if isinstance(key, list):
|
||||
if isinstance(key[0], sexpdata.Symbol) and key[0].value() == "nets":
|
||||
return key[1:]
|
||||
return None
|
||||
|
||||
|
||||
def unpack(sexpr):
|
||||
ret = {}
|
||||
for net in sexpr:
|
||||
code = net[1][1]
|
||||
name = net[2][1]
|
||||
if isinstance(name, sexpdata.Symbol):
|
||||
name = name.value()
|
||||
name = str(name)
|
||||
|
||||
members = []
|
||||
if len(net) < 4:
|
||||
continue
|
||||
|
||||
for node in net[3:]:
|
||||
ref = node[1][1]
|
||||
pin = node[2][1]
|
||||
|
||||
if isinstance(ref, sexpdata.Symbol):
|
||||
ref = ref.value()
|
||||
|
||||
if isinstance(pin, sexpdata.Symbol):
|
||||
pin = pin.value()
|
||||
|
||||
ref = str(ref)
|
||||
pin = str(pin)
|
||||
|
||||
members.append((ref, pin))
|
||||
|
||||
members.sort()
|
||||
ret[name] = members
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='Compare KiCad netlists')
|
||||
parser.add_argument('first_netlist')
|
||||
parser.add_argument('second_netlist')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
fn_a = args.first_netlist
|
||||
fn_b = args.second_netlist
|
||||
|
||||
with open(fn_a, 'r') as f:
|
||||
a = sexpdata.load(f)
|
||||
|
||||
with open(fn_b, 'r') as f:
|
||||
b = sexpdata.load(f)
|
||||
|
||||
# Extract the nets portion
|
||||
a = extract_nets(a)
|
||||
b = extract_nets(b)
|
||||
|
||||
if a is None:
|
||||
print("Could not read nets from {}".format(fn_a))
|
||||
sys.exit(1)
|
||||
|
||||
if b is None:
|
||||
print("Could not read nets from {}".format(fn_b))
|
||||
sys.exit(1)
|
||||
|
||||
nets_a = unpack(a)
|
||||
nets_b = unpack(b)
|
||||
|
||||
sa = set(nets_a.keys())
|
||||
sb = set(nets_b.keys())
|
||||
|
||||
only_a = sa - sb
|
||||
only_b = sb - sa
|
||||
both = sa & sb
|
||||
|
||||
if len(only_a) == len(only_b) == 0:
|
||||
print("{} and {} are identical".format(fn_a, fn_b))
|
||||
sys.exit(0)
|
||||
|
||||
print("A: {}\nB: {}".format(fn_a, fn_b))
|
||||
|
||||
changed_header = False
|
||||
|
||||
for net_name in sorted(both):
|
||||
if nets_a[net_name] != nets_b[net_name]:
|
||||
if not changed_header:
|
||||
print("\nChanged nets:\n")
|
||||
changed_header = True
|
||||
|
||||
print("{}: {} => {}".format(net_name, nets_a[net_name],
|
||||
nets_b[net_name]))
|
||||
|
||||
discards_a = set()
|
||||
discards_b = set()
|
||||
|
||||
renamed_header = False
|
||||
|
||||
for net_name in sorted(only_a):
|
||||
for candidate in only_b:
|
||||
if nets_a[net_name] == nets_b[candidate]:
|
||||
if not renamed_header:
|
||||
print("\nRenamed nets (no connection changes):\n")
|
||||
renamed_header = True
|
||||
|
||||
print("{} => {}".format(net_name, candidate))
|
||||
discards_a.add(net_name)
|
||||
discards_b.add(candidate)
|
||||
|
||||
only_a.difference_update(discards_a)
|
||||
only_b.difference_update(discards_b)
|
||||
|
||||
if len(only_a) > 0:
|
||||
print("\nOnly in {}:\n".format(fn_a))
|
||||
print('\n'.join(["{}: {}".format(el, nets_a[el])
|
||||
for el in sorted(only_a)]))
|
||||
|
||||
if len(only_b) > 0:
|
||||
print("\nOnly in {}:\n".format(fn_b))
|
||||
print('\n'.join(["{}: {}".format(el, nets_b[el])
|
||||
for el in sorted(only_b)]))
|
Loading…
Reference in New Issue