| #!/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() |