|  | #!/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 | 
|  | import json | 
|  | import io | 
|  | import os | 
|  | import random | 
|  | random.seed(int(os.getenv("SEED"), 16)) | 
|  | from prjxray import util | 
|  | from prjxray import lut_maker | 
|  | from prjxray import verilog | 
|  | from prjxray.db import Database | 
|  |  | 
|  | from iostandards import * | 
|  |  | 
|  | def gen_sites(): | 
|  | ''' | 
|  | IOB18S: main IOB of a diff pair | 
|  | IOB18M: secondary IOB of a diff pair | 
|  | IOB18: not a diff pair. Relatively rare (at least in ROI...2 of them?) | 
|  | Focus on IOB18S to start | 
|  | ''' | 
|  | db = Database(util.get_db_root(), util.get_part()) | 
|  | grid = db.grid() | 
|  | for tile_name in sorted(grid.tiles()): | 
|  | loc = grid.loc_of_tilename(tile_name) | 
|  | gridinfo = grid.gridinfo_at_loc(loc) | 
|  |  | 
|  | sites = {} | 
|  | for site_name, site_type in gridinfo.sites.items(): | 
|  | if site_type in ['IOB18S', 'IOB18M']: | 
|  | sites[site_type] = site_name | 
|  |  | 
|  | if sites: | 
|  | yield tile_name, sites | 
|  |  | 
|  |  | 
|  | def write_params(params): | 
|  | pinstr = 'tile,site,pin,iostandard,drive,slew,pulltype\n' | 
|  | for vals in params: | 
|  | pinstr += ','.join(map(str, vals)) + '\n' | 
|  |  | 
|  | open('params.csv', 'w').write(pinstr) | 
|  |  | 
|  |  | 
|  | def run(): | 
|  | tile_types = ['IBUF', 'OBUF', 'IOBUF_DCIEN', None, None] | 
|  |  | 
|  | i_idx = 0 | 
|  | o_idx = 0 | 
|  | io_idx = 0 | 
|  |  | 
|  | iostandards = LVCMOS + SSTL + LVDS | 
|  |  | 
|  | diff_map = { | 
|  | "SSTL15":  DIFF_SSTL15, | 
|  | "SSTL135": DIFF_SSTL135, | 
|  | "SSTL12":  DIFF_SSTL12, | 
|  | } | 
|  |  | 
|  | vref_map = { | 
|  | "SSTL12":        .600, | 
|  | "SSTL135":       .675, | 
|  | "SSTL15":        .75, | 
|  | "DIFF_SSTL12":   .600, | 
|  | "DIFF_SSTL135":  .675, | 
|  | "DIFF_SSTL15":   .75, | 
|  | } | 
|  |  | 
|  | only_diff_map = { | 
|  | "LVDS": ["LVDS"], | 
|  | } | 
|  |  | 
|  | slews = ['FAST', 'SLOW'] | 
|  | pulls = ["NONE", "KEEPER", "PULLDOWN", "PULLUP"] | 
|  |  | 
|  | luts = lut_maker.LutMaker() | 
|  |  | 
|  | connects = io.StringIO() | 
|  |  | 
|  | tile_params = [] | 
|  | params = { | 
|  | "tiles": [], | 
|  | 'INTERNAL_VREF': {}, | 
|  | } | 
|  |  | 
|  | with open(os.path.join(os.getenv('FUZDIR'), 'build', 'iobanks.txt')) as f: | 
|  | iobanks = set() | 
|  | iob_sites = dict() | 
|  | for l in f: | 
|  | fields = l.split(',') | 
|  | iob_site = fields[0] | 
|  | iobank = fields[1].rstrip() | 
|  |  | 
|  | iobanks.add(iobank) | 
|  | iob_sites[iob_site] = iobank | 
|  |  | 
|  | params['iobanks'] = iobanks | 
|  |  | 
|  | iostandard_map = dict() | 
|  | for iobank in iobanks: | 
|  | iostandard = random.choice(iostandards) | 
|  | if iostandard in SSTL: | 
|  | params['INTERNAL_VREF'][iobank] = vref_map[iostandard] | 
|  |  | 
|  | iostandard_map[iobank] = iostandard | 
|  |  | 
|  | params['iobanks'] = list(iobanks) | 
|  |  | 
|  | any_idelay = False | 
|  | for tile, sites in gen_sites(): | 
|  | iostandard = None | 
|  |  | 
|  | site_bels = {} | 
|  | for site_type, site_name in sites.items(): | 
|  | iobank = iob_sites[site_name] | 
|  | iostandard = iostandard_map[iobank] | 
|  |  | 
|  | if iostandard in ['LVCMOS12']: | 
|  | drives = [2, 4, 6, 8] | 
|  | elif iostandard in ['LVCMOS15', 'LVCMOS18']: | 
|  | drives = [2, 4, 6, 8, 12, 16] | 
|  | elif iostandard in LVDS + SSTL: | 
|  | drives = None | 
|  | else: | 
|  | assert False, f"Unhandled iostandard: {iostandard}" | 
|  |  | 
|  | if site_type.endswith('M'): | 
|  | if iostandard in diff_map: | 
|  | site_bels[site_type] = random.choice( | 
|  | tile_types + ['IBUFDS', 'OBUFDS', 'OBUFTDS']) | 
|  | elif iostandard in only_diff_map: | 
|  | site_bels[site_type] = random.choice( | 
|  | ['IBUFDS', 'OBUFDS', 'OBUFTDS', None, None]) | 
|  | else: | 
|  | site_bels[site_type] = random.choice(tile_types) | 
|  | is_m_diff = site_bels[site_type] is not None and site_bels[ | 
|  | site_type].endswith('DS') | 
|  | else: | 
|  | site_bels[site_type] = random.choice(tile_types) | 
|  |  | 
|  | if is_m_diff or iostandard in only_diff_map: | 
|  | site_bels['IOB18S'] = None | 
|  |  | 
|  | for site_type, site in sites.items(): | 
|  | p = {} | 
|  | p['tile'] = tile | 
|  | p['site'] = site | 
|  | p['type'] = site_bels[site_type] | 
|  |  | 
|  | if p['type'] is not None and p['type'].endswith('DS'): | 
|  | if iostandard in diff_map: | 
|  | iostandard_site = random.choice(diff_map[iostandard]) | 
|  | elif iostandard in only_diff_map: | 
|  | iostandard_site = random.choice(only_diff_map[iostandard]) | 
|  | p['pair_site'] = sites['IOB18S'] | 
|  | else: | 
|  | iostandard_site = iostandard | 
|  |  | 
|  | p['IOSTANDARD'] = verilog.quote(iostandard_site) | 
|  | p['PULLTYPE'] = verilog.quote(random.choice(pulls)) | 
|  |  | 
|  | if p['type'] is None: | 
|  | p['pad_wire'] = None | 
|  | elif p['type'] == 'IBUF': | 
|  | p['pad_wire'] = 'di[{}]'.format(i_idx) | 
|  | p['IDELAY_ONLY'] = random.randint(0, 1) | 
|  | if not p['IDELAY_ONLY']: | 
|  | p['owire'] = luts.get_next_input_net() | 
|  | else: | 
|  | any_idelay = True | 
|  | p['owire'] = 'idelay_{site}'.format(**p) | 
|  |  | 
|  | p['DRIVE'] = None | 
|  | p['SLEW'] = None | 
|  | p['IBUF_LOW_PWR'] = random.randint(0, 1) | 
|  | i_idx += 1 | 
|  | elif p['type'] == 'IBUFDS': | 
|  | p['pad_wire'] = 'di[{}]'.format(i_idx) | 
|  | i_idx += 1 | 
|  | p['bpad_wire'] = 'di[{}]'.format(i_idx) | 
|  | i_idx += 1 | 
|  |  | 
|  | p['IDELAY_ONLY'] = random.randint(0, 1) | 
|  | p['DIFF_TERM'] = random.randint(0, 1) if iostandard in LVDS else 0 | 
|  | if not p['IDELAY_ONLY']: | 
|  | p['owire'] = luts.get_next_input_net() | 
|  | else: | 
|  | any_idelay = True | 
|  | p['owire'] = 'idelay_{site}'.format(**p) | 
|  |  | 
|  | p['DRIVE'] = None | 
|  | p['SLEW'] = None | 
|  | p['IBUF_LOW_PWR'] = random.randint(0, 1) | 
|  |  | 
|  | elif p['type'] == 'OBUF': | 
|  | p['pad_wire'] = 'do[{}]'.format(o_idx) | 
|  | p['iwire'] = luts.get_next_output_net() | 
|  | if drives is not None: | 
|  | p['DRIVE'] = random.choice(drives) | 
|  | else: | 
|  | p['DRIVE'] = None | 
|  | p['SLEW'] = verilog.quote(random.choice(slews)) | 
|  |  | 
|  | o_idx += 1 | 
|  | elif p['type'] == 'OBUFDS': | 
|  | p['pad_wire'] = 'do[{}]'.format(o_idx) | 
|  | o_idx += 1 | 
|  | p['bpad_wire'] = 'do[{}]'.format(o_idx) | 
|  | o_idx += 1 | 
|  | p['iwire'] = luts.get_next_output_net() | 
|  | if drives is not None: | 
|  | p['DRIVE'] = random.choice(drives) | 
|  | else: | 
|  | p['DRIVE'] = None | 
|  | p['SLEW'] = verilog.quote(random.choice(slews)) | 
|  | elif p['type'] == 'OBUFTDS': | 
|  | p['pad_wire'] = 'do[{}]'.format(o_idx) | 
|  | o_idx += 1 | 
|  | p['bpad_wire'] = 'do[{}]'.format(o_idx) | 
|  | o_idx += 1 | 
|  | p['tristate_wire'] = random.choice( | 
|  | ('0', luts.get_next_output_net())) | 
|  | p['iwire'] = luts.get_next_output_net() | 
|  | if drives is not None: | 
|  | p['DRIVE'] = random.choice(drives) | 
|  | else: | 
|  | p['DRIVE'] = None | 
|  | p['SLEW'] = verilog.quote(random.choice(slews)) | 
|  | elif p['type'] == 'IOBUF_DCIEN': | 
|  | p['pad_wire'] = 'dio[{}]'.format(io_idx) | 
|  | p['iwire'] = luts.get_next_output_net() | 
|  | p['owire'] = luts.get_next_input_net() | 
|  | if drives is not None: | 
|  | p['DRIVE'] = random.choice(drives) | 
|  | else: | 
|  | p['DRIVE'] = None | 
|  | p['SLEW'] = verilog.quote(random.choice(slews)) | 
|  | p['tristate_wire'] = random.choice( | 
|  | ('0', luts.get_next_output_net())) | 
|  | p['ibufdisable_wire'] = random.choice( | 
|  | ('0', luts.get_next_output_net())) | 
|  | p['dcitermdisable_wire'] = random.choice( | 
|  | ('0', luts.get_next_output_net())) | 
|  | io_idx += 1 | 
|  |  | 
|  | if 'DRIVE' in p: | 
|  | if p['DRIVE'] is not None: | 
|  | p['DRIVE_STR'] = '.DRIVE({}),'.format(p['DRIVE']) | 
|  | else: | 
|  | p['DRIVE_STR'] = '' | 
|  |  | 
|  | if 'SLEW' in p: | 
|  | p['SLEW_STR'] = '' | 
|  | if iostandard in only_diff_map: | 
|  | p['SLEW'] = None | 
|  | elif p['DRIVE'] is not None: | 
|  | p['SLEW_STR'] = '.SLEW({}),'.format(p['SLEW']) | 
|  |  | 
|  | if p['type'] is not None: | 
|  | tile_params.append( | 
|  | ( | 
|  | tile, | 
|  | site, | 
|  | p['pad_wire'], | 
|  | iostandard_site, | 
|  | p['DRIVE'], | 
|  | verilog.unquote(p['SLEW']) if p['SLEW'] else None, | 
|  | verilog.unquote(p['PULLTYPE']), | 
|  | )) | 
|  | params['tiles'].append(p) | 
|  |  | 
|  | write_params(tile_params) | 
|  |  | 
|  | with open('iobank_vref.csv', 'w') as f: | 
|  | for iobank, vref in params['INTERNAL_VREF'].items(): | 
|  | f.write('{},{}\n'.format(iobank, vref)) | 
|  |  | 
|  | print( | 
|  | ''' | 
|  | `define N_DI {n_di} | 
|  | `define N_DO {n_do} | 
|  | `define N_DIO {n_dio} | 
|  |  | 
|  | module top(input wire [`N_DI-1:0] di, output wire [`N_DO-1:0] do, inout wire [`N_DIO-1:0] dio, input refclk); | 
|  | '''.format(n_di=i_idx, n_do=o_idx, n_dio=io_idx)) | 
|  |  | 
|  | if any_idelay: | 
|  | print(''' | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | IDELAYCTRL(.REFCLK(refclk));''') | 
|  |  | 
|  | # Always output a LUT6 to make placer happy. | 
|  | print(''' | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | LUT6 dummy_lut();''') | 
|  |  | 
|  | for p in params['tiles']: | 
|  | if p['type'] is None: | 
|  | continue | 
|  | elif p['type'] == 'IBUF': | 
|  | print( | 
|  | ''' | 
|  | wire idelay_{site}; | 
|  |  | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | IBUF #( | 
|  | .IBUF_LOW_PWR({IBUF_LOW_PWR}), | 
|  | .IOSTANDARD({IOSTANDARD}) | 
|  | ) ibuf_{site} ( | 
|  | .I({pad_wire}), | 
|  | .O({owire}) | 
|  | );'''.format(**p), | 
|  | file=connects) | 
|  | if p['IDELAY_ONLY']: | 
|  | print( | 
|  | """ | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | IDELAYE2 idelay_site_{site} ( | 
|  | .IDATAIN(idelay_{site}) | 
|  | );""".format(**p), | 
|  | file=connects) | 
|  |  | 
|  | elif p['type'] == 'IBUFDS': | 
|  | print( | 
|  | ''' | 
|  | wire idelay_{site}; | 
|  |  | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | IBUFDS #( | 
|  | .IBUF_LOW_PWR({IBUF_LOW_PWR}), | 
|  | .DIFF_TERM({DIFF_TERM}), | 
|  | .IOSTANDARD({IOSTANDARD}) | 
|  | ) ibuf_{site} ( | 
|  | .I({pad_wire}), | 
|  | .IB({bpad_wire}), | 
|  | .O({owire}) | 
|  | );'''.format(**p), | 
|  | file=connects) | 
|  | if p['IDELAY_ONLY']: | 
|  | print( | 
|  | """ | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | IDELAYE2 idelay_site_{site} ( | 
|  | .IDATAIN(idelay_{site}) | 
|  | );""".format(**p), | 
|  | file=connects) | 
|  |  | 
|  | elif p['type'] == 'OBUF': | 
|  | print( | 
|  | ''' | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | OBUF #( | 
|  | {DRIVE_STR} | 
|  | {SLEW_STR} | 
|  | .IOSTANDARD({IOSTANDARD}) | 
|  | ) obuf_{site} ( | 
|  | .O({pad_wire}), | 
|  | .I({iwire}) | 
|  | );'''.format(**p), | 
|  | file=connects) | 
|  | elif p['type'] == 'OBUFDS': | 
|  | print( | 
|  | ''' | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | OBUFDS #( | 
|  | {DRIVE_STR} | 
|  | {SLEW_STR} | 
|  | .IOSTANDARD({IOSTANDARD}) | 
|  | ) obufds_{site} ( | 
|  | .O({pad_wire}), | 
|  | .OB({bpad_wire}), | 
|  | .I({iwire}) | 
|  | );'''.format(**p), | 
|  | file=connects) | 
|  | elif p['type'] == 'OBUFTDS': | 
|  | print( | 
|  | ''' | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | OBUFTDS #( | 
|  | {DRIVE_STR} | 
|  | {SLEW_STR} | 
|  | .IOSTANDARD({IOSTANDARD}) | 
|  | ) obufds_{site} ( | 
|  | .O({pad_wire}), | 
|  | .OB({bpad_wire}), | 
|  | .T({tristate_wire}), | 
|  | .I({iwire}) | 
|  | );'''.format(**p), | 
|  | file=connects) | 
|  | elif p['type'] == 'IOBUF_DCIEN': | 
|  | print( | 
|  | ''' | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | IOBUF_DCIEN #( | 
|  | {DRIVE_STR} | 
|  | {SLEW_STR} | 
|  | .IOSTANDARD({IOSTANDARD}) | 
|  | ) ibuf_{site} ( | 
|  | .IO({pad_wire}), | 
|  | .I({iwire}), | 
|  | .O({owire}), | 
|  | .T({tristate_wire}), | 
|  | .IBUFDISABLE({ibufdisable_wire}), | 
|  | .DCITERMDISABLE({dcitermdisable_wire}) | 
|  | );'''.format(**p), | 
|  | file=connects) | 
|  |  | 
|  | for l in luts.create_wires_and_luts(): | 
|  | print(l) | 
|  |  | 
|  | print(connects.getvalue()) | 
|  |  | 
|  | print("endmodule") | 
|  |  | 
|  | with open('params.json', 'w') as f: | 
|  | json.dump(params, f, indent=2) | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | run() |