diff --git a/fuzzers/074-dump_all/reduce_tile_types.py b/fuzzers/074-dump_all/reduce_tile_types.py index 5b73460d..f8aaec4d 100644 --- a/fuzzers/074-dump_all/reduce_tile_types.py +++ b/fuzzers/074-dump_all/reduce_tile_types.py @@ -18,6 +18,7 @@ import multiprocessing import os import functools import json +from prjxray.xjson import extract_numbers def check_and_strip_prefix(name, prefix): @@ -367,7 +368,8 @@ def main(): reduced_tile['sites'] = sorted( reduced_tile['sites'], - key=lambda site: '{}_{}'.format(site['name'], site['prefix'])) + key=lambda site: extract_numbers( + '{}_{}'.format(site['name'], site['prefix']))) with open(tile_type_file, 'w') as f: json.dump(reduced_tile, f, indent=2, sort_keys=True) diff --git a/minitests/roi_harness/create_design_json.py b/minitests/roi_harness/create_design_json.py index 0c5e8770..1c30fbdf 100644 --- a/minitests/roi_harness/create_design_json.py +++ b/minitests/roi_harness/create_design_json.py @@ -6,12 +6,14 @@ import fasm from prjxray.db import Database from prjxray.roi import Roi from prjxray.util import get_db_root, get_part +from prjxray.xjson import extract_numbers def set_port_wires(ports, name, pin, wires_outside_roi): for port in ports: if name == port['name']: - port['wires_outside_roi'] = sorted(wires_outside_roi) + port['wires_outside_roi'] = sorted( + wires_outside_roi, key=extract_numbers) assert port['pin'] == pin return @@ -106,9 +108,10 @@ def main(): design_json['required_features'] = sorted( fasm.fasm_tuple_to_string(required_features, - canonical=True).split('\n')) + canonical=True).split('\n'), + key=extract_numbers) - design_json['ports'].sort(key=lambda x: x['name']) + design_json['ports'].sort(key=lambda x: extract_numbers(x['name'])) xjson.pprint(sys.stdout, design_json) diff --git a/prjxray/xjson.py b/prjxray/xjson.py new file mode 100644 index 00000000..6bdd3e19 --- /dev/null +++ b/prjxray/xjson.py @@ -0,0 +1,107 @@ +import io +import json +import re + +from collections import OrderedDict + + +def extract_numbers(s): + """ + >>> extract_numbers("CLK_HROW_WR10END2_3") + ('CLK_HROW_WR', 10, 'END', 2, '_', 3) + >>> extract_numbers("VBRK_WR1END2") + ('VBRK_WR', 1, 'END', 2) + """ + bits = [] + for m in re.finditer("([^0-9]*)([0-9]*)", s): + if m.group(1): + bits.append(m.group(1)) + if m.group(2): + bits.append(int(m.group(2))) + return tuple(bits) + + +def sort(data): + """Sort data types via "natural" numbers. + + Supports all the basic Python data types. + >>> o = sort({ + ... 't1': {'c','b'}, # Set + ... 't2': ('a2', 'a10', 'e'), # Tuple + ... 't3': [5, 3, 2], # List + ... 't4': { # Dictionary + ... 'a4': ('b2', 'b3'), + ... 'a2': ['c1', 'c2', 'c0', 'c10'], + ... }, + ... 't5': ['a1b5', 'a2b1', 'a1b1'], + ... }) + >>> for t in o: + ... print(t+':', o[t]) + t1: ('b', 'c') + t2: ('a2', 'a10', 'e') + t3: (5, 3, 2) + t4: OrderedDict([('a2', ('c1', 'c2', 'c0', 'c10')), ('a4', ('b2', 'b3'))]) + t5: ('a1b5', 'a2b1', 'a1b1') + + Don't mangle "pairs" + >>> sort([('b', 'c'), ('2', '1')]) + (('b', 'c'), ('2', '1')) + """ + # FIXME: We assume that a list is a tileconn.json format... + if isinstance(data, list) and len(data) > 0 and 'wire_pairs' in data[0]: + for o in data: + o['wire_pairs'].sort( + key=lambda o: (extract_numbers(o[0]), extract_numbers(o[1]))) + + data.sort(key=lambda o: (o['tile_types'], o['grid_deltas'])) + return data + else: + + def key(o): + if o is None: + return None + elif isinstance(o, str): + return extract_numbers(o) + elif isinstance(o, int): + return o + elif isinstance(o, (list, tuple)): + return tuple(key(i) for i in o) + elif isinstance(o, dict): + return tuple((key(k), key(v)) for k, v in o.items()) + elif isinstance(o, set): + return tuple(key(k) for k in o) + raise ValueError(repr(o)) + + def rsorter(o): + if isinstance(o, dict): + nitems = [] + for k, v in o.items(): + nitems.append((key(k), k, rsorter(v))) + nitems.sort(key=lambda n: n[0]) + + new_dict = OrderedDict() + for _, k, v in nitems: + new_dict[k] = v + return new_dict + + elif isinstance(o, set): + return tuple(sorted((rsorter(v) for v in o), key=key)) + elif isinstance(o, (tuple, list)): + return tuple(rsorter(v) for v in o) + else: + return o + + return rsorter(data) + + +def pprint(f, data): + detach = False + if not isinstance(f, io.TextIOBase): + detach = True + f = io.TextIOWrapper(f) + data = sort(data) + json.dump(data, f, indent=4) + f.write('\n') + f.flush() + if detach: + f.detach() diff --git a/utils/xjson.py b/utils/xjson.py index 03be3ace..7affd767 100755 --- a/utils/xjson.py +++ b/utils/xjson.py @@ -1,113 +1,7 @@ #!/usr/bin/env python3 -import io -import json -import re import sys - -from collections import OrderedDict - - -def extract_numbers(s): - """ - >>> extract_numbers("CLK_HROW_WR10END2_3") - ('CLK_HROW_WR', 10, 'END', 2, '_', 3) - >>> extract_numbers("VBRK_WR1END2") - ('VBRK_WR', 1, 'END', 2) - """ - bits = [] - for m in re.finditer("([^0-9]*)([0-9]*)", s): - if m.group(1): - bits.append(m.group(1)) - if m.group(2): - bits.append(int(m.group(2))) - return tuple(bits) - - -def sort(data): - """Sort data types via "natural" numbers. - - Supports all the basic Python data types. - >>> o = sort({ - ... 't1': {'c','b'}, # Set - ... 't2': ('a2', 'a10', 'e'), # Tuple - ... 't3': [5, 3, 2], # List - ... 't4': { # Dictionary - ... 'a4': ('b2', 'b3'), - ... 'a2': ['c1', 'c2', 'c0', 'c10'], - ... }, - ... 't5': ['a1b5', 'a2b1', 'a1b1'], - ... }) - >>> for t in o: - ... print(t+':', o[t]) - t1: ('b', 'c') - t2: ('a2', 'a10', 'e') - t3: (5, 3, 2) - t4: OrderedDict([('a2', ('c1', 'c2', 'c0', 'c10')), ('a4', ('b2', 'b3'))]) - t5: ('a1b5', 'a2b1', 'a1b1') - - Don't mangle "pairs" - >>> sort([('b', 'c'), ('2', '1')]) - (('b', 'c'), ('2', '1')) - """ - # FIXME: We assume that a list is a tileconn.json format... - if isinstance(data, list) and len(data) > 0 and 'wire_pairs' in data[0]: - for o in data: - o['wire_pairs'].sort( - key=lambda o: (extract_numbers(o[0]), extract_numbers(o[1]))) - - data.sort(key=lambda o: (o['tile_types'], o['grid_deltas'])) - return data - else: - - def key(o): - if o is None: - return None - elif isinstance(o, str): - return extract_numbers(o) - elif isinstance(o, int): - return o - elif isinstance(o, (list, tuple)): - return tuple(key(i) for i in o) - elif isinstance(o, dict): - return tuple((key(k), key(v)) for k, v in o.items()) - elif isinstance(o, set): - return tuple(key(k) for k in o) - raise ValueError(repr(o)) - - def rsorter(o): - if isinstance(o, dict): - nitems = [] - for k, v in o.items(): - nitems.append((key(k), k, rsorter(v))) - nitems.sort(key=lambda n: n[0]) - - new_dict = OrderedDict() - for _, k, v in nitems: - new_dict[k] = v - return new_dict - - elif isinstance(o, set): - return tuple(sorted((rsorter(v) for v in o), key=key)) - elif isinstance(o, (tuple, list)): - return tuple(rsorter(v) for v in o) - else: - return o - - return rsorter(data) - - -def pprint(f, data): - detach = False - if not isinstance(f, io.TextIOBase): - detach = True - f = io.TextIOWrapper(f) - data = sort(data) - json.dump(data, f, indent=4) - f.write('\n') - f.flush() - if detach: - f.detach() - +import json +from prjxray.xjson import pprint if __name__ == "__main__": if len(sys.argv) == 1: