mirror of https://github.com/openXC7/prjxray.git
252 lines
8.8 KiB
Python
252 lines
8.8 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2017-2022 The Project X-Ray Authors.
|
|
#
|
|
# Use of this source code is governed by a ISC-style
|
|
# license that can be found in the LICENSE file or at
|
|
# https://opensource.org/licenses/ISC
|
|
#
|
|
# SPDX-License-Identifier: ISC
|
|
""" IOB bits are more complicated than can be easily expressed to segmaker.
|
|
There are couple cases that need to be handled here:
|
|
|
|
- There are some bits that are always set for IN-only ports, but are cleared
|
|
selectively for OUT and INOUT ports.
|
|
- There are bits per each IOSTANDARD, in addition to drive patterns. These
|
|
can be merged to provide unique "(IOSTANDARD, DRIVE)" bit sets.
|
|
"""
|
|
import argparse
|
|
|
|
|
|
def get_name(l):
|
|
parts = l.strip().split(' ')
|
|
return parts[0]
|
|
|
|
|
|
def get_site(l):
|
|
return get_name(l).split('.')[1]
|
|
|
|
|
|
def parse_bits(l):
|
|
parts = l.strip().split(' ')
|
|
if parts[1] in ['<0', '<const0>', '<const1>']:
|
|
return frozenset()
|
|
else:
|
|
return frozenset(parts[1:])
|
|
|
|
|
|
def filter_negbits(site, feature, bits):
|
|
lvds_bits = frozenset(['!38_24', "!38_48", "!39_33", '!39_47', '!39_49'])
|
|
if "IOB_Y0" in feature and not "LVDS" in feature:
|
|
bits = bits.difference(lvds_bits)
|
|
if feature.endswith("IOB_Y0.LVCMOS12_LVCMOS15_LVCMOS18.IN"):
|
|
bits = bits.difference(frozenset(['!39_01']))
|
|
if feature.endswith("IOB_Y1.LVCMOS12_LVCMOS15_LVCMOS18.IN"):
|
|
bits = bits.difference(frozenset(['!38_126']))
|
|
|
|
return bits
|
|
|
|
|
|
def process_features_sets(iostandard_lines):
|
|
sites = {}
|
|
|
|
for iostd_type, iostd_list in iostandard_lines.items():
|
|
for iostd_line in iostd_list:
|
|
feature = get_name(iostd_line)
|
|
feature_parts = feature.split('.')
|
|
site = get_site(iostd_line)
|
|
iostandard = feature_parts[2]
|
|
|
|
bits = parse_bits(iostd_line)
|
|
|
|
key = (site, iostd_type)
|
|
if key not in sites:
|
|
sites[key] = {}
|
|
|
|
group = feature_parts[3]
|
|
if group not in sites[key]:
|
|
sites[key][group] = {}
|
|
|
|
if group in ['DRIVE', 'SLEW']:
|
|
enum = feature_parts[4]
|
|
sites[key][group][(iostandard, enum)] = bits
|
|
elif group in ['IN', 'IN_DIFF', 'IN_ONLY', 'IN_USE', 'OUT',
|
|
'STEPDOWN', 'ZIBUF_LOW_PWR']:
|
|
sites[key][group][(iostandard, None)] = bits
|
|
else:
|
|
assert False, group
|
|
|
|
for site in sites:
|
|
for iostandard, enum in sites[site]['DRIVE']:
|
|
sites[site]['DRIVE'][(iostandard, enum)] |= sites[site]['OUT'][(
|
|
iostandard, None)]
|
|
|
|
for iostandard, enum in sites[site]['IN']:
|
|
sites[site]['IN_ONLY'][(iostandard, enum)] -= sites[site]['IN'][(
|
|
iostandard, enum)]
|
|
|
|
common_bits = {}
|
|
for site, iostd_type in sites:
|
|
for group in sites[(site, iostd_type)]:
|
|
if (site, group) not in common_bits:
|
|
common_bits[(site, group)] = set()
|
|
|
|
for bits in sites[(site, iostd_type)][group].values():
|
|
common_bits[(site, group)] |= bits
|
|
|
|
slew_in_drives = {}
|
|
|
|
for site, iostd_type in sites:
|
|
common_bits[(site, 'IN')] |= common_bits[(site, 'IN_DIFF')]
|
|
common_bits[(site, 'IN_DIFF')] |= common_bits[(site, 'IN')]
|
|
|
|
# Only DIFF IOSTANDARDS such as LVDS or TMDS do not have DRIVE or SLEW features
|
|
if iostd_type == "NORMAL":
|
|
key = (site, iostd_type)
|
|
common_bits[(site, 'DRIVE')] -= common_bits[(site, 'SLEW')]
|
|
common_bits[(site, 'IN_ONLY')] |= common_bits[(site, 'DRIVE')]
|
|
|
|
for iostandard, enum in sites[key]['DRIVE']:
|
|
slew_in_drive = common_bits[
|
|
(site, 'SLEW')] & sites[key]['DRIVE'][(iostandard, enum)]
|
|
if slew_in_drive:
|
|
if (key, iostandard) not in slew_in_drives:
|
|
slew_in_drives[(key, iostandard)] = set()
|
|
|
|
slew_in_drives[(key, iostandard)] |= slew_in_drive
|
|
sites[key]['DRIVE'][(iostandard, enum)] -= slew_in_drive
|
|
|
|
for site, iostandard in slew_in_drives:
|
|
for _, enum in sites[site]['SLEW']:
|
|
sites[site]['SLEW'][(iostandard,
|
|
enum)] |= slew_in_drives[(site, iostandard)]
|
|
|
|
for site in sites:
|
|
for iostandard, enum in sites[site]['DRIVE']:
|
|
sites[site]['DRIVE'][(iostandard, enum)] |= sites[site]['IN_USE'][(
|
|
iostandard, None)]
|
|
|
|
for iostandard, enum in sites[site]['IN']:
|
|
_, iostd_type = site
|
|
if iostd_type == "ONLY_DIFF":
|
|
sites[site]['IN_DIFF'][(iostandard, enum)] = \
|
|
sites[site]['IN'][(iostandard, enum)]
|
|
elif sites[site]['IN_DIFF'][(iostandard, enum)]:
|
|
sites[site]['IN_DIFF'][(iostandard, enum)] |= \
|
|
sites[site]['IN'][(iostandard, enum)]
|
|
|
|
for site, iostd_type in sites:
|
|
if iostd_type == "NORMAL":
|
|
del sites[(site, iostd_type)]['OUT']
|
|
|
|
allow_zero = ['SLEW']
|
|
|
|
common_groups = dict()
|
|
for site, iostd_type in sites:
|
|
if site not in common_groups:
|
|
common_groups[site] = dict()
|
|
|
|
key = (site, iostd_type)
|
|
for group in sites[key]:
|
|
if iostd_type == "ONLY_DIFF" and group == "IN":
|
|
continue
|
|
|
|
# Merge features that are identical.
|
|
#
|
|
# For example:
|
|
#
|
|
# IOB18.IOB_Y1.LVCMOS15.IN 38_42 39_41
|
|
# IOB18.IOB_Y1.LVCMOS18.IN 38_42 39_41
|
|
#
|
|
# Must be grouped.
|
|
for (iostandard, enum), bits in sites[key][group].items():
|
|
if (bits, group) not in common_groups[site]:
|
|
common_groups[site][(bits, group)] = {
|
|
'IOSTANDARDS': set(),
|
|
'enums': set(),
|
|
}
|
|
|
|
common_groups[site][(bits,
|
|
group)]['IOSTANDARDS'].add(iostandard)
|
|
if enum is not None:
|
|
common_groups[site][(bits, group)]['enums'].add(enum)
|
|
|
|
visited_iostandards = list()
|
|
for site, groups in common_groups.items():
|
|
for (bits, group), v in groups.items():
|
|
iostandards = v['IOSTANDARDS']
|
|
enums = v['enums']
|
|
|
|
# It happens that some features appear only in one of the IOB sites and not
|
|
# in the other. This makes it hard to assign the correct features to the correct
|
|
# site in the P&R toolchain.
|
|
#
|
|
# The following code makes sure that the same set of iostandards
|
|
# (even if not really present at a site location) appears for each site
|
|
for visited_iostandard, visited_group, visited_enums in visited_iostandards:
|
|
same_enum = enums == visited_enums
|
|
same_group = group == visited_group
|
|
compatible_iostd = any(
|
|
x in iostandards for x in visited_iostandard)
|
|
take_visited_iostd = len(visited_iostandard) > len(iostandards)
|
|
if same_enum and same_group and compatible_iostd and take_visited_iostd:
|
|
iostandards = visited_iostandard
|
|
break
|
|
|
|
visited_iostandards.append((iostandards, group, enums))
|
|
|
|
iostandards_string = '_'.join(sorted(iostandards))
|
|
|
|
if enums:
|
|
feature = 'IOB18.{site}.{iostandards}.{group}.{enums}'.format(
|
|
site=site,
|
|
iostandards=iostandards_string,
|
|
group=group,
|
|
enums='_'.join(sorted(enums)),
|
|
)
|
|
else:
|
|
feature = 'IOB18.{site}.{iostandards}.{group}'.format(
|
|
site=site,
|
|
iostandards=iostandards_string,
|
|
group=group,
|
|
)
|
|
|
|
if not bits and group not in allow_zero:
|
|
continue
|
|
|
|
neg_bits = frozenset(
|
|
'!{}'.format(b) for b in (common_bits[(site, group)] - bits))
|
|
neg_bits = filter_negbits(site, feature, neg_bits)
|
|
print('{} {}'.format(feature, ' '.join(sorted(bits | neg_bits))))
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Convert IOB rdb into good rdb."
|
|
"")
|
|
parser.add_argument('input_rdb')
|
|
|
|
args = parser.parse_args()
|
|
|
|
iostandard_lines = {
|
|
"NORMAL": list(),
|
|
"ONLY_DIFF": list(),
|
|
}
|
|
|
|
with open(args.input_rdb) as f:
|
|
for l in f:
|
|
if ('.SSTL' in l or '.LVCMOS' in l
|
|
or '.LVTTL' in l) and 'IOB_' in l:
|
|
iostandard_lines["NORMAL"].append(l)
|
|
elif ('.TMDS' in l or 'LVDS' in l):
|
|
iostandard_lines["ONLY_DIFF"].append(l)
|
|
else:
|
|
print(l.strip())
|
|
|
|
process_features_sets(iostandard_lines)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|