|  | #!/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 | 
|  |  | 
|  | from prjxray.segmaker import Segmaker | 
|  | from prjxray import segmaker | 
|  | from prjxray import verilog | 
|  | import os | 
|  | import json | 
|  | import csv | 
|  |  | 
|  | from iostandards import * | 
|  |  | 
|  | def bitfilter(frame, word): | 
|  | return 38 <= frame | 
|  |  | 
|  | def mk_drive_opt(iostandard, drive): | 
|  | if drive is None: | 
|  | drive = '_FIXED' | 
|  | return '{}.DRIVE.I{}'.format(iostandard, drive) | 
|  |  | 
|  | def drives_for_iostandard(iostandard): | 
|  | if iostandard in ['LVCMOS18', 'LVCMOS15']: | 
|  | drives = [2, 4, 6, 8, 12, 16] | 
|  | elif iostandard == 'LVCMOS12': | 
|  | drives = [2, 4, 6, 8] | 
|  | elif iostandard in SSTL + DIFF_SSTL: | 
|  | return ['_FIXED'] | 
|  | else: | 
|  | assert False, "this line should be unreachable" | 
|  |  | 
|  | return drives | 
|  |  | 
|  | STEPDOWN_IOSTANDARDS   = LVCMOS + SSTL | 
|  | IBUF_LOW_PWR_SUPPORTED = LVDS + SSTL | 
|  | ONLY_DIFF_IOSTANDARDS  = LVDS | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | # Create map of iobank -> sites | 
|  | iobanks = {} | 
|  | site_to_iobank = {} | 
|  | iobank_iostandards = {} | 
|  | with open(os.path.join(os.getenv('FUZDIR'), 'build', 'iobanks.txt')) as f: | 
|  | for l in f: | 
|  | iob_site, iobank = l.strip().split(',') | 
|  | iobank = int(iobank) | 
|  |  | 
|  | if iobank not in iobanks: | 
|  | iobanks[iobank] = set() | 
|  |  | 
|  | iobanks[iobank].add(iob_site) | 
|  | assert iob_site not in site_to_iobank | 
|  | site_to_iobank[iob_site] = iobank | 
|  |  | 
|  | for iobank in iobanks: | 
|  | iobank_iostandards[iobank] = set() | 
|  |  | 
|  | # Load a list of PUDC_B pin function tiles. They are configured differently | 
|  | # by the vendor tools so need to be skipped | 
|  | pudc_tiles = set() | 
|  | with open(os.path.join(os.getenv('FUZDIR'), 'build', | 
|  | 'pudc_sites.csv')) as f: | 
|  | for l in csv.DictReader(f): | 
|  | pudc_tiles.add(l["tile"]) | 
|  |  | 
|  | print("Loading tags") | 
|  | segmk = Segmaker("design.bits") | 
|  | ''' | 
|  | port,site,tile,pin,slew,drive,pulltype | 
|  | di[0],IOB_X1Y107,RIOB18_X1Y107,AF4,PULLDOWN | 
|  | di[10],IOB_X1Y147,RIOB18_X1Y147,U5,PULLUP | 
|  | ''' | 
|  | with open('params.json', 'r') as f: | 
|  | design = json.load(f) | 
|  |  | 
|  | diff_pairs = set() | 
|  | for d in design['tiles']: | 
|  | iostandard = verilog.unquote(d['IOSTANDARD']) | 
|  | if iostandard.startswith('DIFF_'): | 
|  | diff_pairs.add(d['pair_site']) | 
|  |  | 
|  | for d in design['tiles']: | 
|  | site = d['site'] | 
|  | tile = d['tile'] | 
|  |  | 
|  | if tile in pudc_tiles: | 
|  | continue | 
|  |  | 
|  | if site in diff_pairs: | 
|  | continue | 
|  |  | 
|  | iostandard = verilog.unquote(d['IOSTANDARD']) | 
|  | if iostandard.startswith('DIFF_'): | 
|  | iostandard = iostandard[5:] | 
|  |  | 
|  | iobank_iostandards[site_to_iobank[site]].add(iostandard) | 
|  |  | 
|  | only_diff_io = iostandard in ONLY_DIFF_IOSTANDARDS | 
|  |  | 
|  | if d['type'] is None: | 
|  | segmk.add_site_tag(site, 'INOUT', 0) | 
|  | segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 0) | 
|  | segmk.add_site_tag(site, '{}.IN'.format(iostandard), 0) | 
|  | segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 0) | 
|  | segmk.add_site_tag(site, '{}.IN_ONLY'.format(iostandard), 0) | 
|  | elif d['type'] == 'IBUF': | 
|  | segmk.add_site_tag(site, 'INOUT', 0) | 
|  | segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 1) | 
|  | segmk.add_site_tag(site, '{}.IN'.format(iostandard), 1) | 
|  | segmk.add_site_tag(site, '{}.IN_DIFF'.format(iostandard), 0) | 
|  | segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 0) | 
|  | segmk.add_site_tag(site, '{}.IN_ONLY'.format(iostandard), 1) | 
|  | segmk.add_tile_tag(tile, 'IN_DIFF', 0) | 
|  |  | 
|  | if iostandard in IBUF_LOW_PWR_SUPPORTED: | 
|  | segmk.add_site_tag(site, 'IBUF_LOW_PWR', d['IBUF_LOW_PWR']) | 
|  | segmk.add_site_tag(site, 'ZIBUF_LOW_PWR', 1 ^ d['IBUF_LOW_PWR']) | 
|  |  | 
|  | elif d['type'] == 'IBUFDS': | 
|  | psite = d['pair_site'] | 
|  | segmk.add_site_tag(site,  'INOUT', 0) | 
|  | segmk.add_site_tag(site,  '{}.IN_USE'.format(iostandard), 1) | 
|  | segmk.add_site_tag(site,  '{}.IN'.format(iostandard), 1) | 
|  | segmk.add_site_tag(site,  '{}.IN_DIFF'.format(iostandard), 1) | 
|  | segmk.add_site_tag(psite, '{}.IN_DIFF'.format(iostandard), 1) | 
|  | segmk.add_site_tag(site,  '{}.OUT'.format(iostandard), 0) | 
|  | segmk.add_site_tag(site,  '{}.IN_ONLY'.format(iostandard), 1) | 
|  | segmk.add_tile_tag(tile,  'IN_DIFF', 1) | 
|  |  | 
|  | if iostandard in IBUF_LOW_PWR_SUPPORTED: | 
|  | segmk.add_tile_tag(tile, 'DIFF.IBUF_LOW_PWR', d['IBUF_LOW_PWR']) | 
|  | segmk.add_tile_tag(tile, 'DIFF.ZIBUF_LOW_PWR', 1 ^ d['IBUF_LOW_PWR']) | 
|  |  | 
|  | elif d['type'] == 'OBUF': | 
|  | segmk.add_site_tag(site, 'INOUT', 0) | 
|  | segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 1) | 
|  | segmk.add_site_tag(site, '{}.IN'.format(iostandard), 0) | 
|  | segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 1) | 
|  | segmk.add_tile_tag(tile, 'OUT_DIFF', 0) | 
|  |  | 
|  | elif d['type'] == 'OBUFDS': | 
|  | segmk.add_site_tag(site, 'INOUT', 0) | 
|  | segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 1) | 
|  | segmk.add_site_tag(site, '{}.IN'.format(iostandard), 0) | 
|  | segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 1) | 
|  | segmk.add_tile_tag(tile, 'OUT_DIFF', 1 and not only_diff_io) | 
|  | segmk.add_tile_tag(tile, 'OUT_TDIFF', 0) | 
|  |  | 
|  | elif d['type'] == 'OBUFTDS': | 
|  | segmk.add_site_tag(site, 'INOUT', 0) | 
|  | segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 1) | 
|  | segmk.add_site_tag(site, '{}.IN'.format(iostandard), 0) | 
|  | segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 1) | 
|  | segmk.add_tile_tag(tile, 'OUT_DIFF', 1 and not only_diff_io) | 
|  | segmk.add_tile_tag(tile, 'OUT_TDIFF', 1 and not only_diff_io) | 
|  |  | 
|  | elif d['type'] == 'IOBUF_DCIEN': | 
|  | segmk.add_site_tag(site, 'INOUT', 1) | 
|  | segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 1) | 
|  | segmk.add_site_tag(site, '{}.IN'.format(iostandard), 1) | 
|  | segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 1) | 
|  |  | 
|  |  | 
|  | if d['type'] is not None: | 
|  | segmaker.add_site_group_zero( | 
|  | segmk, site, "PULLTYPE.", | 
|  | ("NONE", "KEEPER", "PULLDOWN", "PULLUP"), "PULLDOWN", | 
|  | verilog.unquote(d['PULLTYPE'])) | 
|  |  | 
|  | if d['type'] in [None, 'IBUF', 'IBUFDS']: | 
|  | continue | 
|  |  | 
|  | drive_opts = set() | 
|  | for opt in LVCMOS: | 
|  | for drive_opt in ("2", "4", "6", "8", "12", "16"): | 
|  | if drive_opt in ["12", "16"] and opt == "LVCMOS12": | 
|  | continue | 
|  |  | 
|  | drive_opts.add(mk_drive_opt(opt, drive_opt)) | 
|  |  | 
|  | for sstl in SSTL: | 
|  | drive_opts.add(mk_drive_opt(sstl, None)) | 
|  |  | 
|  | drive_opts.add(mk_drive_opt("LVDS", None)) | 
|  |  | 
|  | segmaker.add_site_group_zero( | 
|  | segmk, site, '', drive_opts, mk_drive_opt('LVCMOS25', '12'), | 
|  | mk_drive_opt(iostandard, d['DRIVE'])) | 
|  |  | 
|  | if d['SLEW']: | 
|  | for opt in ["SLOW", "FAST"]: | 
|  | segmk.add_site_tag( | 
|  | site, iostandard + ".SLEW." + opt, | 
|  | opt == verilog.unquote(d['SLEW'])) | 
|  |  | 
|  | if 'ibufdisable_wire' in d: | 
|  | segmk.add_site_tag( | 
|  | site, 'IBUFDISABLE.I', d['ibufdisable_wire'] != '0') | 
|  |  | 
|  | if 'dcitermdisable_wire' in d: | 
|  | segmk.add_site_tag( | 
|  | site, 'DCITERMDISABLE.I', d['dcitermdisable_wire'] != '0') | 
|  |  | 
|  | site_to_cmt = {} | 
|  | site_to_tile = {} | 
|  | tile_to_cmt = {} | 
|  | cmt_to_idelay = {} | 
|  | with open(os.path.join(os.getenv('FUZDIR'), 'build', 'cmt_regions.csv')) as f: | 
|  | for l in f: | 
|  | site, tile, cmt = l.strip().split(',') | 
|  | site_to_tile[site] = tile | 
|  |  | 
|  | site_to_cmt[site] = cmt | 
|  | tile_to_cmt[tile] = cmt | 
|  |  | 
|  | # Given IDELAYCTRL's are only located in HCLK_IOI tiles, and | 
|  | # there is only on HCLK_IOI tile per CMT, update | 
|  | # CMT -> IDELAYCTRL / tile map. | 
|  | if 'IDELAYCTRL' in site: | 
|  | assert cmt not in cmt_to_idelay | 
|  | cmt_to_idelay[cmt] = site, tile | 
|  |  | 
|  | # For each IOBANK with an active VREF set the feature | 
|  | cmt_vref_active = set() | 
|  | with open('iobank_vref.csv') as f: | 
|  | for l in f: | 
|  | iobank, vref = l.strip().split(',') | 
|  | iobank = int(iobank) | 
|  |  | 
|  | cmt = None | 
|  | for cmt_site in iobanks[iobank]: | 
|  | if cmt_site in site_to_cmt: | 
|  | cmt = site_to_cmt[cmt_site] | 
|  | break | 
|  |  | 
|  | if cmt is None: | 
|  | continue | 
|  |  | 
|  | cmt_vref_active.add(cmt) | 
|  |  | 
|  | _, hclk_cmt_tile = cmt_to_idelay[cmt] | 
|  |  | 
|  | opt = 'VREF.V_{:d}_MV'.format(int(float(vref) * 1000)) | 
|  | segmk.add_tile_tag(hclk_cmt_tile, opt, 1) | 
|  |  | 
|  | for iobank in iobank_iostandards: | 
|  | if len(iobank_iostandards[iobank]) == 0: | 
|  | continue | 
|  |  | 
|  | for cmt_site in iobanks[iobank]: | 
|  | if cmt_site in site_to_cmt: | 
|  | cmt = site_to_cmt[cmt_site] | 
|  | break | 
|  |  | 
|  | if cmt is None: | 
|  | continue | 
|  |  | 
|  | _, hclk_cmt_tile = cmt_to_idelay[cmt] | 
|  |  | 
|  | assert len(iobank_iostandards[iobank]) == 1, iobank_iostandards[iobank] | 
|  |  | 
|  | iostandard = list(iobank_iostandards[iobank])[0] | 
|  | for only_diff_io in ONLY_DIFF_IOSTANDARDS: | 
|  | segmk.add_tile_tag( | 
|  | hclk_cmt_tile, '{}_IN_USE'.format(only_diff_io), | 
|  | iostandard == only_diff_io) | 
|  |  | 
|  | segmk.add_tile_tag( | 
|  | hclk_cmt_tile, 'ONLY_DIFF_IN_USE', | 
|  | iostandard in ONLY_DIFF_IOSTANDARDS) | 
|  |  | 
|  | # For IOBANK's with no active VREF, clear all VREF options. | 
|  | for cmt, (_, hclk_cmt_tile) in cmt_to_idelay.items(): | 
|  | if cmt in cmt_vref_active: | 
|  | continue | 
|  |  | 
|  | for vref in ( | 
|  | .600, | 
|  | .675, | 
|  | .75, | 
|  | .90, | 
|  | ): | 
|  | opt = 'VREF.V_{:d}_MV'.format(int(vref * 1000)) | 
|  | segmk.add_tile_tag(hclk_cmt_tile, opt, 0) | 
|  |  | 
|  | segmk.compile(bitfilter=bitfilter) | 
|  | segmk.write(allow_empty=True) | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | main() |