|  | #!/usr/bin/env python3 | 
|  | # -*- coding: utf-8 -*- | 
|  | # | 
|  | # Copyright (C) 2017-2020  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 os | 
|  | import random | 
|  | random.seed(int(os.getenv("SEED"), 16)) | 
|  | from prjxray import util | 
|  | from prjxray.db import Database | 
|  |  | 
|  |  | 
|  | class PipList(object): | 
|  | def __init__(self): | 
|  | self.piplist = {} | 
|  | self.ppiplist = {} | 
|  |  | 
|  | def get_pip_and_ppip_list_for_tile_type(self, tile_type): | 
|  |  | 
|  | # Load PIP list for the tile type if not already loaded | 
|  | if tile_type not in self.piplist: | 
|  | self.piplist[tile_type] = [] | 
|  |  | 
|  | fname = os.path.join( | 
|  | os.getenv('FUZDIR'), '..', 'piplist', 'build', 'cmt_top', | 
|  | tile_type.lower() + '.txt') | 
|  |  | 
|  | with open(fname, "r") as f: | 
|  | for l in f: | 
|  | tile, dst, src = l.strip().split('.') | 
|  | if tile_type == tile: | 
|  | self.piplist[tile_type].append((src, dst)) | 
|  |  | 
|  | # Load PPIP list for the tile type if not already loaded | 
|  | if tile_type not in self.ppiplist: | 
|  | self.ppiplist[tile_type] = [] | 
|  |  | 
|  | fname = os.path.join( | 
|  | os.getenv('FUZDIR'), '..', '071-ppips', 'build', | 
|  | 'ppips_' + tile_type.lower() + '.db') | 
|  |  | 
|  | with open(fname, "r") as f: | 
|  | for l in f: | 
|  | pip_data, pip_type = l.strip().split() | 
|  |  | 
|  | if pip_type != 'always': | 
|  | continue | 
|  |  | 
|  | tile, dst, src = pip_data.strip().split('.') | 
|  | if tile_type == tile: | 
|  | self.ppiplist[tile_type].append((src, dst)) | 
|  |  | 
|  | return self.piplist[tile_type], self.ppiplist[tile_type] | 
|  |  | 
|  |  | 
|  | def find_phasers_for_pll(grid, loc): | 
|  | gridinfo = grid.gridinfo_at_loc((loc[0], loc[1] + 13)) | 
|  |  | 
|  | phasers = { | 
|  | 'IN': [], | 
|  | 'OUT': [], | 
|  | } | 
|  |  | 
|  | for site_name, site_type in gridinfo.sites.items(): | 
|  | if site_type == 'PHASER_IN_PHY': | 
|  | phasers['IN'].append(site_name) | 
|  | elif site_type == 'PHASER_OUT_PHY': | 
|  | phasers['OUT'].append(site_name) | 
|  |  | 
|  | assert len(phasers['IN']) > 0 | 
|  | assert len(phasers['OUT']) > 0 | 
|  |  | 
|  | phasers['IN'].sort() | 
|  | phasers['OUT'].sort() | 
|  |  | 
|  | return phasers | 
|  |  | 
|  |  | 
|  | def gen_sites(): | 
|  | 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) | 
|  |  | 
|  | for site_name, site_type in gridinfo.sites.items(): | 
|  | if site_type in ['PLLE2_ADV']: | 
|  | phasers = find_phasers_for_pll(grid, loc) | 
|  | yield tile_name, site_name, phasers | 
|  |  | 
|  |  | 
|  | def get_random_route_from_site_pin( | 
|  | pip_list, tile_name, site_pin, direction, occupied_wires): | 
|  |  | 
|  | # A map of PLL site pins to wires they are connected to. | 
|  | pin_to_wire = { | 
|  | "CMT_TOP_L_UPPER_T": { | 
|  | "CLKIN1": "CMT_TOP_R_UPPER_T_PLLE2_CLKIN1", | 
|  | "CLKIN2": "CMT_TOP_R_UPPER_T_PLLE2_CLKIN2", | 
|  | "CLKFBIN": "CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN", | 
|  | }, | 
|  | "CMT_TOP_R_UPPER_T": { | 
|  | "CLKIN1": "CMT_TOP_R_UPPER_T_PLLE2_CLKIN1", | 
|  | "CLKIN2": "CMT_TOP_R_UPPER_T_PLLE2_CLKIN2", | 
|  | "CLKFBIN": "CMT_TOP_R_UPPER_T_PLLE2_CLKFBIN", | 
|  | }, | 
|  | } | 
|  |  | 
|  | # Get tile type | 
|  | tile_type = tile_name.rsplit("_", maxsplit=1)[0] | 
|  |  | 
|  | # Get all PIPs (PIPs + PPIPs) | 
|  | pips, ppips = pip_list.get_pip_and_ppip_list_for_tile_type(tile_type) | 
|  | all_pips = pips + ppips | 
|  |  | 
|  | # The first wire | 
|  | wire = pin_to_wire[tile_type][site_pin] | 
|  |  | 
|  | # Walk randomly. | 
|  | route = [] | 
|  | while True: | 
|  | route.append(wire) | 
|  |  | 
|  | wires = [] | 
|  |  | 
|  | for src, dst in all_pips: | 
|  | if direction == "down" and src == wire: | 
|  | next_wire = dst | 
|  | elif direction == "up" and dst == wire: | 
|  | next_wire = src | 
|  | else: | 
|  | continue | 
|  |  | 
|  | if next_wire not in occupied_wires: | 
|  | wires.append(next_wire) | 
|  |  | 
|  | if len(wires) == 0: | 
|  | break | 
|  |  | 
|  | wire = random.choice(wires) | 
|  | occupied_wires.add(wire) | 
|  |  | 
|  | # For "up" direction reverse the route. | 
|  | if direction == "down": | 
|  | return route | 
|  | if direction == "up": | 
|  | return route[::-1] | 
|  |  | 
|  |  | 
|  | def main(): | 
|  |  | 
|  | # 8 inputs per clock region | 
|  | # 5 clock regions for device | 
|  | max_clk_inputs = 8 * 5 | 
|  | clkin_idx = 0 | 
|  |  | 
|  | print( | 
|  | ''' | 
|  | module top( | 
|  | input wire [{nclkin}:0] clkin | 
|  | ); | 
|  |  | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | LUT6 dummy(); | 
|  | '''.format(nclkin=max_clk_inputs - 1)) | 
|  |  | 
|  | pip_list = PipList() | 
|  | bufg_count = 0 | 
|  |  | 
|  | design_file = open('design.txt', 'w') | 
|  | routes_file = open('routes.txt', 'w') | 
|  |  | 
|  | for tile, site, phasers in sorted(gen_sites(), key=lambda x: x[0]): | 
|  | in_use = random.randint(0, 2) > 0 | 
|  |  | 
|  | design_file.write("{},{},{}\n".format(tile, site, int(in_use))) | 
|  |  | 
|  | if not in_use: | 
|  | continue | 
|  |  | 
|  | # Generate random routes to/from some pins | 
|  | routes = {} | 
|  | endpoints = {} | 
|  |  | 
|  | pins = [ | 
|  | ('CLKIN1', 'up'), | 
|  | ('CLKIN2', 'up'), | 
|  | ('CLKFBIN', 'up'), | 
|  |  | 
|  | # Sometimes manually randomized route for CLKOUTx conflicts with | 
|  | # the verilog design. | 
|  | #('CLKOUT0', 'down'), | 
|  | #('CLKOUT1', 'down'), | 
|  | #('CLKOUT2', 'down'), | 
|  | #('CLKOUT3', 'down'), | 
|  | ] | 
|  |  | 
|  | occupied_wires = set() | 
|  | for pin, dir in pins: | 
|  |  | 
|  | route = get_random_route_from_site_pin( | 
|  | pip_list, tile, pin, dir, occupied_wires) | 
|  | if route is None: | 
|  | endpoints[pin] = "" | 
|  | continue | 
|  |  | 
|  | routes[pin] = ( | 
|  | route, | 
|  | dir, | 
|  | ) | 
|  | endpoints[pin] = route[-1] if dir == 'down' else route[0] | 
|  |  | 
|  | internal_feedback = endpoints['CLKFBIN'].endswith('CLKFBOUT') | 
|  |  | 
|  | # Store them in a random order so the TCL script will try to route | 
|  | # them also in the random order. | 
|  | lines = [] | 
|  | for pin, ( | 
|  | route, | 
|  | dir, | 
|  | ) in routes.items(): | 
|  |  | 
|  | route_str = " ".join(route) | 
|  | lines.append( | 
|  | '{} {} {} {} {}\n'.format(tile, site, pin, dir, route_str)) | 
|  |  | 
|  | random.shuffle(lines) | 
|  | routes_file.writelines(lines) | 
|  |  | 
|  | clkfbin_src = random.choice(('BUFH', 'logic')) | 
|  |  | 
|  | if internal_feedback: | 
|  | COMPENSATION = "INTERNAL" | 
|  | else: | 
|  | if clkfbin_src == 'logic': | 
|  | COMPENSATION = 'EXTERNAL' | 
|  | else: | 
|  | COMPENSATION = "ZHOLD" | 
|  |  | 
|  | print( | 
|  | """ | 
|  | wire clkfbin_{site}; | 
|  | wire clkin1_{site}; | 
|  | wire clkin2_{site}; | 
|  | wire clkfbout_mult_{site}; | 
|  | wire clkout0_{site}; | 
|  | wire clkout1_{site}; | 
|  | wire clkout2_{site}; | 
|  | wire clkout3_{site}; | 
|  | wire clkout4_{site}; | 
|  | wire clkout5_{site}; | 
|  | (* KEEP, DONT_TOUCH, LOC = "{site}" *) | 
|  | PLLE2_ADV #( | 
|  | .COMPENSATION("{COMPENSATION}") | 
|  | ) pll_{site} ( | 
|  | .CLKFBOUT(clkfbout_mult_{site}), | 
|  | .CLKOUT0(clkout0_{site}), | 
|  | .CLKOUT1(clkout1_{site}), | 
|  | .CLKOUT2(clkout2_{site}), | 
|  | .CLKOUT3(clkout3_{site}), | 
|  | .CLKOUT4(clkout4_{site}), | 
|  | .CLKOUT5(clkout5_{site}), | 
|  | .DRDY(), | 
|  | .LOCKED(), | 
|  | .DO(), | 
|  | .CLKFBIN(clkfbin_{site}), | 
|  | .CLKIN1(clkin1_{site}), | 
|  | .CLKIN2(clkin2_{site}), | 
|  | .CLKINSEL(), | 
|  | .DCLK(), | 
|  | .DEN(), | 
|  | .DWE(), | 
|  | .PWRDWN(), | 
|  | .RST(), | 
|  | .DI(), | 
|  | .DADDR()); | 
|  | """.format(site=site, COMPENSATION=COMPENSATION)) | 
|  |  | 
|  | for clkout in range(4, 6): | 
|  | # CLKOUT4 and CLKOUT5 can only drive one signal type | 
|  | if random.randint(0, 1) and bufg_count < 16: | 
|  | bufg_count += 1 | 
|  | print( | 
|  | """ | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | BUFG ( | 
|  | .I(clkout{idx}_{site}) | 
|  | );""".format(idx=clkout, site=site)) | 
|  |  | 
|  | any_phaser = False | 
|  |  | 
|  | for clkout in range(4): | 
|  | # CLKOUT0-CLKOUT3 can drive: | 
|  | #  - Global drivers (e.g. BUFG) | 
|  | #  - PHASER_[IN|OUT]_[CA|DB]_FREQREFCLK via BB_[0-3] | 
|  | drive_bufg = random.randint(0, 1) and bufg_count < 16 | 
|  | drive_phaser = 0  #random.randint(0, 1) | 
|  |  | 
|  | if drive_bufg: | 
|  | bufg_count += 1 | 
|  | print( | 
|  | """ | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | BUFG ( | 
|  | .I(clkout{idx}_{site}) | 
|  | );""".format(idx=clkout, site=site)) | 
|  |  | 
|  | if drive_phaser and not any_phaser and False: | 
|  | any_phaser = True | 
|  | print( | 
|  | """ | 
|  | (* KEEP, DONT_TOUCH, LOC="{phaser_loc}" *) | 
|  | PHASER_OUT phaser_{site}( | 
|  | .FREQREFCLK(clkout{idx}_{site}) | 
|  | );""".format(idx=clkout, site=site, phaser_loc=phasers['OUT'][0])) | 
|  |  | 
|  | if internal_feedback: | 
|  | print( | 
|  | """ | 
|  | assign clkfbin_{site} = clkfbout_mult_{site}; | 
|  | """.format(site=site)) | 
|  | else: | 
|  | if clkfbin_src == 'BUFH': | 
|  | print( | 
|  | """ | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | BUFH ( | 
|  | .I(clkfbout_mult_{site}), | 
|  | .O(clkfbin_{site}) | 
|  | );""".format(site=site)) | 
|  | elif clkfbin_src == 'logic': | 
|  | print( | 
|  | """ | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | LUT6 # (.INIT(64'h5555555555555555)) | 
|  | clkfbin_logic_{site} ( | 
|  | .I0(clkfbout_mult_{site}), | 
|  | .O(clkfbin_{site}) | 
|  | ); | 
|  | """.format(site=site)) | 
|  | else: | 
|  | assert False, clkfb_src | 
|  |  | 
|  | for clkin in range(2): | 
|  | clkin_src = random.choice(( | 
|  | 'BUFH', | 
|  | 'BUFR', | 
|  | 'logic', | 
|  | )) | 
|  |  | 
|  | if clkin_src == 'BUFH': | 
|  | print( | 
|  | """ | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | BUFH ( | 
|  | .O(clkin{idx}_{site}), | 
|  | .I(clkin{idx2}) | 
|  | );""".format(idx=clkin + 1, idx2=clkin_idx, site=site)) | 
|  | elif clkin_src == 'BUFR': | 
|  | print( | 
|  | """ | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | BUFR ( | 
|  | .O(clkin{idx}_{site}), | 
|  | .I(clkin{idx2}) | 
|  | );""".format(idx=clkin + 1, idx2=clkin_idx, site=site)) | 
|  | elif clkin_src == 'logic': | 
|  | print( | 
|  | """ | 
|  | (* KEEP, DONT_TOUCH *) | 
|  | LUT6 # (.INIT(64'h5555555555555555)) | 
|  | clkin{idx}_logic_{site} ( | 
|  | .I0(clkin{idx2}), | 
|  | .O(clkin{idx}_{site}) | 
|  | ); | 
|  | """.format(idx=clkin + 1, idx2=clkin_idx, site=site)) | 
|  | else: | 
|  | assert False, (clkin, clkin_src) | 
|  |  | 
|  | clkin_idx += 1 | 
|  |  | 
|  | print("endmodule") | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | main() |