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