|  | #!/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 os | 
|  | import random | 
|  | import math | 
|  | random.seed(int(os.getenv("SEED"), 16)) | 
|  | from prjxray import util | 
|  | from prjxray import verilog | 
|  | from prjxray import lut_maker | 
|  | from prjxray.db import Database | 
|  |  | 
|  | NOT_INCLUDED_TILES = ['RIOI_SING'] | 
|  |  | 
|  | SITE_TYPES = ['OLOGICE2', 'ILOGICE2'] | 
|  |  | 
|  |  | 
|  | def read_site_to_cmt(): | 
|  | """ Yields clock sources and which CMT they route within. """ | 
|  | with open(os.path.join(os.getenv('FUZDIR'), 'build', | 
|  | 'cmt_regions.csv')) as f: | 
|  | for l in f: | 
|  | site, cmt = l.strip().split(',') | 
|  | yield (site, cmt) | 
|  |  | 
|  |  | 
|  | def gen_sites(): | 
|  | ''' Return dict of ISERDES/OSERDES locations. ''' | 
|  | db = Database(util.get_db_root(), util.get_part()) | 
|  | grid = db.grid() | 
|  |  | 
|  | xy_fun = util.create_xy_fun('\S+') | 
|  |  | 
|  | tiles = grid.tiles() | 
|  |  | 
|  | for tile_name in sorted(tiles): | 
|  | loc = grid.loc_of_tilename(tile_name) | 
|  | gridinfo = grid.gridinfo_at_loc(loc) | 
|  | tile_type = gridinfo.tile_type | 
|  |  | 
|  | tile = {'tile': tile_name, 'tile_type': tile_type, 'ioi_sites': {}} | 
|  |  | 
|  | for site_name, site_type in gridinfo.sites.items(): | 
|  | if site_type in SITE_TYPES: | 
|  | xy = xy_fun(site_name) | 
|  | if xy not in tile['ioi_sites']: | 
|  | tile['ioi_sites'][xy] = {} | 
|  |  | 
|  | tile['ioi_sites'][xy][site_type] = site_name | 
|  |  | 
|  | yield tile | 
|  |  | 
|  |  | 
|  | class ClockSources(object): | 
|  | def __init__(self): | 
|  | self.site_to_cmt = dict(read_site_to_cmt()) | 
|  |  | 
|  | self.leaf_gclks = {} | 
|  | self.ioclks = {} | 
|  | self.rclks = {} | 
|  | self.selected_leaf_gclks = {} | 
|  | self.lut_maker = lut_maker.LutMaker() | 
|  |  | 
|  | for cmt in set(self.site_to_cmt.values()): | 
|  | self.leaf_gclks[cmt] = [] | 
|  | self.ioclks[cmt] = [] | 
|  | self.rclks[cmt] = [] | 
|  |  | 
|  | def init_clocks(self): | 
|  | """ Initialize all IOI clock sources. """ | 
|  | for site, cmt in self.site_to_cmt.items(): | 
|  | clk = 'clk_' + site | 
|  | if 'BUFHCE' in site: | 
|  | print( | 
|  | """ | 
|  | wire {clk}; | 
|  | (* KEEP, DONT_TOUCH, LOC = "{site}" *) | 
|  | BUFH bufh_{site}( | 
|  | .O({clk}) | 
|  | ); | 
|  | """.format( | 
|  | clk=clk, | 
|  | site=site, | 
|  | )) | 
|  |  | 
|  | self.leaf_gclks[cmt].append(clk) | 
|  |  | 
|  | if 'BUFIO' in site: | 
|  | print( | 
|  | """ | 
|  | wire {clk}; | 
|  | (* KEEP, DONT_TOUCH, LOC = "{site}" *) | 
|  | BUFIO bufio_{site}( | 
|  | .O({clk}) | 
|  | ); | 
|  | """.format( | 
|  | clk=clk, | 
|  | site=site, | 
|  | )) | 
|  |  | 
|  | self.ioclks[cmt].append(clk) | 
|  |  | 
|  | if 'BUFR' in site: | 
|  | print( | 
|  | """ | 
|  | wire {clk}; | 
|  | (* KEEP, DONT_TOUCH, LOC = "{site}" *) | 
|  | BUFR bufr_{site}( | 
|  | .O({clk}) | 
|  | ); | 
|  | """.format( | 
|  | clk=clk, | 
|  | site=site, | 
|  | )) | 
|  |  | 
|  | self.rclks[cmt].append(clk) | 
|  |  | 
|  | # Choose 6 leaf_gclks to be used in each CMT. | 
|  | for cmt in self.leaf_gclks: | 
|  | self.selected_leaf_gclks[cmt] = random.sample( | 
|  | self.leaf_gclks[cmt], 6) | 
|  |  | 
|  | def get_clock( | 
|  | self, | 
|  | site, | 
|  | allow_ioclks, | 
|  | allow_rclks, | 
|  | allow_fabric=True, | 
|  | allow_empty=True): | 
|  | cmt = self.site_to_cmt[site] | 
|  | choices = [] | 
|  | if allow_fabric: | 
|  | choices.append('lut') | 
|  |  | 
|  | if allow_empty: | 
|  | choices.append('') | 
|  |  | 
|  | choices.extend(self.selected_leaf_gclks[cmt]) | 
|  | if allow_ioclks: | 
|  | choices.extend(self.ioclks[cmt]) | 
|  |  | 
|  | if allow_rclks: | 
|  | choices.extend(self.rclks[cmt]) | 
|  |  | 
|  | clock = random.choice(choices) | 
|  | is_lut = False | 
|  | if clock == "lut": | 
|  | clock = self.lut_maker.get_next_output_net() | 
|  | is_lut = True | 
|  | return clock, is_lut | 
|  |  | 
|  |  | 
|  | def add_port(ports, port, signal): | 
|  | ports.append('.{}({})'.format(port, signal)) | 
|  |  | 
|  |  | 
|  | def run(): | 
|  | print("module top();") | 
|  |  | 
|  | clocks = ClockSources() | 
|  | clocks.init_clocks() | 
|  | """ | 
|  |  | 
|  | ISERDESE2 clock sources: | 
|  |  | 
|  | CLK/CLKB: | 
|  | - Allows LEAF_GCLK, IOCLKS, RCLKS and fabric | 
|  | - Dedicated pips | 
|  |  | 
|  | CLKDIV: | 
|  | - No dedicated pips, uses fabric clock in. | 
|  |  | 
|  | CLKDIVP: | 
|  | - Has pips, MIG only, PHASER or fabric. | 
|  |  | 
|  | OCLK/OCLKB: | 
|  | - Allows LEAF_GCLK, IOCLKS, RCLKS and fabric | 
|  | - Must match OSERDESE2:CLK/CLKB | 
|  |  | 
|  | OSERDESE2 clock sources: | 
|  |  | 
|  | CLKDIV/CLKDIVB: | 
|  | - Allows LEAF_GCLK and RCLKS and fabric | 
|  | - Dedicated pips | 
|  |  | 
|  | CLKDIVF/CLKDIVFB: | 
|  | - Allows LEAF_GCLK and RCLKS and fabric | 
|  | - No explicit port, follows CLKDIV/CLKDIVB? | 
|  | """ | 
|  |  | 
|  | output = [] | 
|  | route_file = open("routes.txt", "w") | 
|  |  | 
|  | for tile in gen_sites(): | 
|  | if tile['tile_type'] in NOT_INCLUDED_TILES: | 
|  | continue | 
|  |  | 
|  | for xy in tile['ioi_sites']: | 
|  | ilogic_site_type = random.choice([None, 'ISERDESE2', 'IDDR']) | 
|  | use_oserdes = random.randint(0, 1) | 
|  |  | 
|  | ilogic_site = tile['ioi_sites'][xy]['ILOGICE2'] | 
|  | ologic_site = tile['ioi_sites'][xy]['OLOGICE2'] | 
|  |  | 
|  | if use_oserdes: | 
|  | oclk, _ = clocks.get_clock( | 
|  | ologic_site, allow_ioclks=True, allow_rclks=True) | 
|  |  | 
|  | oclkb = oclk | 
|  | else: | 
|  | oclk, is_lut = clocks.get_clock( | 
|  | ilogic_site, allow_ioclks=True, allow_rclks=True) | 
|  |  | 
|  | if random.randint(0, 1): | 
|  | oclkb = oclk | 
|  | else: | 
|  | if random.randint(0, 1): | 
|  | oclkb, _ = clocks.get_clock( | 
|  | ilogic_site, | 
|  | allow_ioclks=True, | 
|  | allow_rclks=True, | 
|  | allow_fabric=not is_lut) | 
|  | else: | 
|  | # Explicitly provide IMUX stimulus to resolve IMUX pips | 
|  | oclk = random.randint(0, 1) | 
|  | oclkb = random.randint(0, 1) | 
|  |  | 
|  | DATA_RATE = random.choice(['DDR', 'SDR']) | 
|  | clk, clk_is_lut = clocks.get_clock( | 
|  | ilogic_site, | 
|  | allow_ioclks=True, | 
|  | allow_rclks=True, | 
|  | allow_empty=DATA_RATE == 'SDR') | 
|  |  | 
|  | clkb = clk | 
|  | while clkb == clk: | 
|  | clkb, clkb_is_lut = clocks.get_clock( | 
|  | ilogic_site, | 
|  | allow_ioclks=True, | 
|  | allow_rclks=True, | 
|  | allow_empty=False) | 
|  |  | 
|  | imux_available = { | 
|  | 0: set(("IOI_IMUX20_0", "IOI_IMUX22_0")), | 
|  | 1: set(("IOI_IMUX20_1", "IOI_IMUX22_1")), | 
|  | } | 
|  |  | 
|  | # Force CLK route through IMUX when connected to a LUT | 
|  | if clk_is_lut: | 
|  | y = (xy[1] + 1) % 2 | 
|  |  | 
|  | route = random.choice(list(imux_available[y])) | 
|  | imux_available[y].remove(route) | 
|  |  | 
|  | route = "{}/{}".format(tile["tile"], route) | 
|  | route_file.write("{} {}\n".format(clk, route)) | 
|  |  | 
|  | # Force CLKB route through IMUX when connected to a LUT | 
|  | if clkb_is_lut: | 
|  | y = (xy[1] + 1) % 2 | 
|  |  | 
|  | route = random.choice(list(imux_available[y])) | 
|  | imux_available[y].remove(route) | 
|  |  | 
|  | route = "{}/{}".format(tile["tile"], route) | 
|  | route_file.write("{} {}\n".format(clkb, route)) | 
|  |  | 
|  | if ilogic_site_type is None: | 
|  | pass | 
|  |  | 
|  | elif ilogic_site_type == 'ISERDESE2': | 
|  | INTERFACE_TYPE = random.choice( | 
|  | [ | 
|  | 'MEMORY', | 
|  | 'MEMORY_DDR3', | 
|  | 'MEMORY_QDR', | 
|  | 'NETWORKING', | 
|  | 'OVERSAMPLE', | 
|  | ]) | 
|  | ports = [] | 
|  |  | 
|  | add_port(ports, 'CLK', clk) | 
|  | add_port(ports, 'CLKB', clkb) | 
|  | add_port(ports, 'OCLK', oclk) | 
|  | add_port(ports, 'OCLKB', oclkb) | 
|  |  | 
|  | output.append( | 
|  | """ | 
|  | (* KEEP, DONT_TOUCH, LOC="{site}" *) | 
|  | ISERDESE2 #( | 
|  | .DATA_RATE({DATA_RATE}), | 
|  | .INTERFACE_TYPE({INTERFACE_TYPE}), | 
|  | .IS_CLK_INVERTED({IS_CLK_INVERTED}), | 
|  | .IS_CLKB_INVERTED({IS_CLKB_INVERTED}), | 
|  | .IS_OCLK_INVERTED({IS_OCLK_INVERTED}), | 
|  | .IS_OCLKB_INVERTED({IS_OCLKB_INVERTED}), | 
|  | .INIT_Q1({INIT_Q1}), | 
|  | .INIT_Q2({INIT_Q2}), | 
|  | .INIT_Q3({INIT_Q3}), | 
|  | .INIT_Q4({INIT_Q4}), | 
|  | .SRVAL_Q1({SRVAL_Q1}), | 
|  | .SRVAL_Q2({SRVAL_Q2}), | 
|  | .SRVAL_Q3({SRVAL_Q3}), | 
|  | .SRVAL_Q4({SRVAL_Q4}) | 
|  | ) iserdes_{site}( | 
|  | {ports});""".format( | 
|  | site=ilogic_site, | 
|  | ports=',\n'.join(ports), | 
|  | DATA_RATE=verilog.quote(DATA_RATE), | 
|  | INTERFACE_TYPE=verilog.quote(INTERFACE_TYPE), | 
|  | IS_CLK_INVERTED=random.randint(0, 1), | 
|  | IS_CLKB_INVERTED=random.randint(0, 1), | 
|  | IS_OCLK_INVERTED=random.randint(0, 1), | 
|  | IS_OCLKB_INVERTED=random.randint(0, 1), | 
|  | INIT_Q1=random.randint(0, 1), | 
|  | INIT_Q2=random.randint(0, 1), | 
|  | INIT_Q3=random.randint(0, 1), | 
|  | INIT_Q4=random.randint(0, 1), | 
|  | SRVAL_Q1=random.randint(0, 1), | 
|  | SRVAL_Q2=random.randint(0, 1), | 
|  | SRVAL_Q3=random.randint(0, 1), | 
|  | SRVAL_Q4=random.randint(0, 1), | 
|  | )) | 
|  | elif ilogic_site_type == 'IDDR': | 
|  | ports = [] | 
|  | add_port(ports, 'C', clk) | 
|  | add_port(ports, 'CB', clkb) | 
|  |  | 
|  | output.append( | 
|  | """ | 
|  | (* KEEP, DONT_TOUCH, LOC="{site}" *) | 
|  | IDDR_2CLK #( | 
|  | .INIT_Q1({INIT_Q1}), | 
|  | .INIT_Q2({INIT_Q2}), | 
|  | .SRTYPE({SRTYPE}) | 
|  | ) iserdes_{site}( | 
|  | {ports});""".format( | 
|  | site=ilogic_site, | 
|  | ports=',\n'.join(ports), | 
|  | INIT_Q1=random.randint(0, 1), | 
|  | INIT_Q2=random.randint(0, 1), | 
|  | SRTYPE=verilog.quote(random.choice(['ASYNC', 'SYNC'])), | 
|  | )) | 
|  | else: | 
|  | assert False, ilogic_site_type | 
|  |  | 
|  | if use_oserdes: | 
|  | ports = [] | 
|  |  | 
|  | add_port( | 
|  | ports, 'CLKDIV', | 
|  | clocks.get_clock( | 
|  | ologic_site, | 
|  | allow_ioclks=False, | 
|  | allow_rclks=True, | 
|  | )[0]) | 
|  |  | 
|  | add_port(ports, 'CLK', oclk) | 
|  |  | 
|  | output.append( | 
|  | """ | 
|  | (* KEEP, DONT_TOUCH, LOC = "{site}" *) | 
|  | OSERDESE2 #( | 
|  | .IS_CLK_INVERTED({IS_CLK_INVERTED}), | 
|  | .DATA_RATE_OQ("SDR"), | 
|  | .DATA_RATE_TQ("SDR") | 
|  | ) oserdes_{site} ( | 
|  | {ports});""".format( | 
|  | IS_CLK_INVERTED=random.randint(0, 1), | 
|  | site=ologic_site, | 
|  | ports=',\n'.join(ports), | 
|  | )) | 
|  |  | 
|  | for s in clocks.lut_maker.create_wires_and_luts(): | 
|  | print(s) | 
|  |  | 
|  | for s in output: | 
|  | print(s) | 
|  |  | 
|  | print("endmodule") | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | run() |