""" Emits top.v's for various BUFHCE routing inputs. """ import os import random random.seed(int(os.getenv("SEED"), 16)) from prjxray import util from prjxray.lut_maker import LutMaker from prjxray.db import Database from io import StringIO CMT_XY_FUN = util.create_xy_fun(prefix='') BUFGCTRL_XY_FUN = util.create_xy_fun('BUFGCTRL_') 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) class ClockSources(object): """ Class for tracking clock sources. Some clock sources can be routed to any CMT, for these, cmt='ANY'. For clock sources that belong to a CMT, cmt should be set to the CMT of the source. """ def __init__(self): self.sources = {} self.merged_sources = {} self.source_to_cmt = {} self.used_sources_from_cmt = {} def add_clock_source(self, source, cmt): """ Adds a source from a specific CMT. cmt='ANY' indicates that this source can be routed to any CMT. """ if cmt not in self.sources: self.sources[cmt] = [] self.sources[cmt].append(source) assert source not in self.source_to_cmt or self.source_to_cmt[ source] == cmt, source self.source_to_cmt[source] = cmt def get_random_source(self, cmt): """ Get a random source that is routable to the specific CMT. get_random_source will return a source that is either cmt='ANY', cmt equal to the input CMT, or the adjecent CMT. """ if cmt not in self.merged_sources: choices = [] if 'ANY' in self.sources: choices.extend(self.sources['ANY']) if cmt in self.sources: choices.extend(self.sources[cmt]) x, y = CMT_XY_FUN(cmt) if x % 2 == 0: x += 1 else: x -= 1 paired_cmt = 'X{}Y{}'.format(x, y) if paired_cmt in self.sources: choices.extend(self.sources[paired_cmt]) self.merged_sources[cmt] = choices if self.merged_sources[cmt]: source = random.choice(self.merged_sources[cmt]) source_cmt = self.source_to_cmt[source] if source_cmt not in self.used_sources_from_cmt: self.used_sources_from_cmt[source_cmt] = set() self.used_sources_from_cmt[source_cmt].add(source) if source_cmt != 'ANY' and len( self.used_sources_from_cmt[source_cmt]) > 14: print('//', self.used_sources_from_cmt) self.used_sources_from_cmt[source_cmt].remove(source) return None else: return source def main(): """ BUFG's can be driven from: Interconnect HROW cascade """ print( ''' module top(); (* KEEP, DONT_TOUCH *) LUT6 dummy(); ''') site_to_cmt = dict(read_site_to_cmt()) luts = LutMaker() wires = StringIO() bufgs = StringIO() clock_sources = ClockSources() db = Database(util.get_db_root()) grid = db.grid() def gen_sites(desired_site_type): for tile_name in sorted(grid.tiles()): loc = grid.loc_of_tilename(tile_name) gridinfo = grid.gridinfo_at_loc(loc) for site, site_type in gridinfo.sites.items(): if site_type == desired_site_type: yield tile_name, site for _, site in gen_sites('MMCME2_ADV'): mmcm_clocks = [ 'mmcm_clock_{site}_{idx}'.format(site=site, idx=idx) for idx in range(13) ] for clk in mmcm_clocks: clock_sources.add_clock_source(clk, site_to_cmt[site]) print( """ wire {c0}, {c1}, {c2}, {c3}, {c4}, {c5}; (* KEEP, DONT_TOUCH, LOC = "{site}" *) MMCME2_ADV pll_{site} ( .CLKOUT0({c0}), .CLKOUT0B({c1}), .CLKOUT1({c2}), .CLKOUT1B({c3}), .CLKOUT2({c4}), .CLKOUT2B({c5}), .CLKOUT3({c6}), .CLKOUT3B({c7}), .CLKOUT4({c8}), .CLKOUT5({c9}), .CLKOUT6({c10}), .CLKFBOUT({c11}), .CLKFBOUTB({c12}) ); """.format( site=site, c0=mmcm_clocks[0], c1=mmcm_clocks[1], c2=mmcm_clocks[2], c3=mmcm_clocks[3], c4=mmcm_clocks[4], c5=mmcm_clocks[5], c6=mmcm_clocks[6], c7=mmcm_clocks[7], c8=mmcm_clocks[8], c9=mmcm_clocks[9], c10=mmcm_clocks[10], c11=mmcm_clocks[11], c12=mmcm_clocks[12], )) for _, site in sorted(gen_sites("BUFGCTRL"), key=lambda x: BUFGCTRL_XY_FUN(x[1])): print( """ wire O_{site}; wire S1_{site}; wire S0_{site}; wire IGNORE1_{site}; wire IGNORE0_{site}; wire I1_{site}; wire I0_{site}; wire CE1_{site}; wire CE0_{site}; """.format(site=site), file=wires) print( """ (* KEEP, DONT_TOUCH, LOC = "{site}" *) BUFGCTRL bufg_{site} ( .O(O_{site}), .S1(S1_{site}), .S0(S0_{site}), .IGNORE1(IGNORE1_{site}), .IGNORE0(IGNORE0_{site}), .I1(I1_{site}), .I0(I0_{site}), .CE1(CE1_{site}), .CE0(CE0_{site}) ); """.format(site=site), file=bufgs) """ BUFG clock sources: 2 from interconnect Output of BUFG +/- 1 Cascade in (e.g. PLL, MMCM) """ CLOCK_CHOICES = ( 'LUT', 'BUFG_+1', 'BUFG_-1', 'CASCADE', ) def find_bufg_cmt(tile): if '_BOT_' in tile: inc = 1 else: inc = -1 loc = grid.loc_of_tilename(tile) offset = 1 while True: gridinfo = grid.gridinfo_at_loc( (loc.grid_x, loc.grid_y + offset * inc)) if gridinfo.tile_type.startswith('CLK_HROW_'): return site_to_cmt[list(gridinfo.sites.keys())[0]] offset += 1 def get_clock_net(tile, site, source_type): if source_type == 'LUT': return luts.get_next_output_net() elif source_type == 'BUFG_+1': x, y = BUFGCTRL_XY_FUN(site) target_y = y + 1 max_y = ((y // 16) + 1) * 16 if target_y >= max_y: target_y -= 16 return 'O_BUFGCTRL_X{x}Y{y}'.format(x=x, y=target_y) elif source_type == 'BUFG_-1': x, y = BUFGCTRL_XY_FUN(site) target_y = y - 1 min_y = (y // 16) * 16 if target_y < min_y: target_y += 16 return 'O_BUFGCTRL_X{x}Y{y}'.format(x=x, y=target_y) elif source_type == 'CASCADE': cmt = find_bufg_cmt(tile) return clock_sources.get_random_source(cmt) else: assert False, source_type for tile, site in sorted(gen_sites("BUFGCTRL"), key=lambda x: BUFGCTRL_XY_FUN(x[1])): if random.randint(0, 1): print( """ assign I0_{site} = {i0_net};""".format( site=site, i0_net=get_clock_net( tile, site, random.choice(CLOCK_CHOICES))), file=bufgs) if random.randint(0, 1): print( """ assign I1_{site} = {i1_net};""".format( site=site, i1_net=get_clock_net( tile, site, random.choice(CLOCK_CHOICES))), file=bufgs) print( """ assign S0_{site} = {s0_net}; assign S1_{site} = {s1_net}; assign IGNORE0_{site} = {ignore0_net}; assign IGNORE1_{site} = {ignore1_net}; assign CE0_{site} = {ce0_net}; assign CE1_{site} = {ce1_net}; """.format( site=site, s0_net=luts.get_next_output_net(), s1_net=luts.get_next_output_net(), ignore0_net=luts.get_next_output_net(), ignore1_net=luts.get_next_output_net(), ce0_net=luts.get_next_output_net(), ce1_net=luts.get_next_output_net(), ), file=bufgs) for l in luts.create_wires_and_luts(): print(l) print(wires.getvalue()) print(bufgs.getvalue()) itr = iter(gen_sites('BUFHCE')) for tile, site in sorted(gen_sites("BUFGCTRL"), key=lambda x: BUFGCTRL_XY_FUN(x[1])): if random.randint(0, 1): _, bufhce_site = next(itr) print( """ (* KEEP, DONT_TOUCH, LOC = "{bufhce_site}" *) BUFHCE bufhce_{bufhce_site} ( .I(O_{site}) );""".format( site=site, bufhce_site=bufhce_site, )) print("endmodule") if __name__ == '__main__': main()