text: #!/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
  
  INT_TILE_TYPES = ['INT_L', 'INT_R']
  HCLK_TILE_TYPES = ['HCLK_L', 'HCLK_R', 'HCLK_L_BOT_UTURN', 'HCLK_R_BOT_UTURN']
  
  
  def get_int_column_roots(grid):
      for tile_name in sorted(grid.tiles()):
          loc = grid.loc_of_tilename(tile_name)
          gridinfo = grid.gridinfo_at_loc(loc)
  
          if gridinfo.tile_type not in INT_TILE_TYPES:
              continue
  
          next_gridinfo = grid.gridinfo_at_loc((loc.grid_x, loc.grid_y + 1))
  
          if next_gridinfo.tile_type in INT_TILE_TYPES:
              continue
  
          if next_gridinfo.tile_type in HCLK_TILE_TYPES:
              continue
  
          assert next_gridinfo.tile_type in [
              'B_TERM_INT', 'BRKH_INT', 'BRKH_B_TERM_INT'
          ], next_gridinfo.tile_type
  
          yield tile_name
  
  
  def build_int_columns(grid):
  
      int_columns = {}
  
      for root_tile_name in get_int_column_roots(grid):
          assert root_tile_name not in int_columns
          int_columns[root_tile_name] = []
  
          tile_name = root_tile_name
          gridinfo = grid.gridinfo_at_tilename(tile_name)
  
          # Walk up INT column.
          while gridinfo.tile_type in INT_TILE_TYPES:
              int_columns[root_tile_name].append(tile_name)
  
              loc = grid.loc_of_tilename(tile_name)
              tile_name = grid.tilename_at_loc((loc.grid_x, loc.grid_y - 1))
              gridinfo = grid.gridinfo_at_tilename(tile_name)
  
              if gridinfo.tile_type in HCLK_TILE_TYPES:
                  loc = grid.loc_of_tilename(tile_name)
                  tile_name = grid.tilename_at_loc((loc.grid_x, loc.grid_y - 1))
                  gridinfo = grid.gridinfo_at_tilename(tile_name)
  
          assert gridinfo.tile_type in [
              'T_TERM_INT', 'BRKH_INT', 'BRKH_TERM_INT'
          ], gridinfo.tile_type
  
      return int_columns
  
  
  def pair_int_tiles(grid, int_tiles):
      tiles_left = set(int_tiles)
  
      while tiles_left:
          tile_name = tiles_left.pop()
  
          gridinfo = grid.gridinfo_at_tilename(tile_name)
          loc = grid.loc_of_tilename(tile_name)
  
          assert gridinfo.tile_type in INT_TILE_TYPES
  
          if gridinfo.tile_type == 'INT_L':
              other_int_tile = 'INT_R'
              other_loc = (loc.grid_x + 1, loc.grid_y)
          else:
              other_int_tile = 'INT_L'
              other_loc = (loc.grid_x - 1, loc.grid_y)
  
          paired_tile_name = grid.tilename_at_loc(other_loc)
          paired_gridinfo = grid.gridinfo_at_tilename(paired_tile_name)
          assert paired_gridinfo.tile_type == other_int_tile, paired_gridinfo.tile_type
  
          tiles_left.remove(paired_tile_name)
  
          yield sorted([tile_name, paired_tile_name])
  
  
  def is_orphan_int_row(grid, int_l, int_r):
      """ Returns true if given INT pair have no adjcent sites. """
  
      loc = grid.loc_of_tilename(int_l)
  
      if grid.gridinfo_at_loc(
          (loc.grid_x - 1, loc.grid_y)).tile_type != 'INT_INTERFACE_L':
          return False
  
      if grid.gridinfo_at_loc(
          (loc.grid_x - 2, loc.grid_y)).tile_type != 'VFRAME':
          return False
  
      loc = grid.loc_of_tilename(int_r)
  
      if grid.gridinfo_at_loc(
          (loc.grid_x + 1, loc.grid_y)).tile_type != 'INT_INTERFACE_R':
          return False
  
      if grid.gridinfo_at_loc(
          (loc.grid_x + 2,
           loc.grid_y)).tile_type not in ['CLK_FEED', 'CLK_BUFG_REBUF', 'NULL']:
          return False
  
      return True
  
  
  def gen_orphan_ints(grid):
      int_columns = build_int_columns(grid)
  
      for int_l_column, int_r_column in sorted(pair_int_tiles(
              grid, int_columns.keys())):
          found_site = False
          for int_l, int_r in zip(int_columns[int_l_column],
                                  int_columns[int_r_column]):
              if not is_orphan_int_row(grid, int_l, int_r):
                  found_site = True
                  break
  
          if not found_site:
              yield int_columns[int_l_column], int_columns[int_r_column]
  
  
  def write_params(params):
      pinstr = 'tile,val\n'
      for tile, (val) in sorted(params.items()):
          pinstr += '%s,%s\n' % (tile, val)
      open('params.csv', 'w').write(pinstr)
  
  
  def build_cross_int(params, grid, int_l, int_r):
      """ Toggles INT_R.ER1BEG1.EE4END0 """
  
      loc = grid.loc_of_tilename(int_r)
  
      origin_tile = grid.tilename_at_loc((loc.grid_x - 4, loc.grid_y))
      origin_gridinfo = grid.gridinfo_at_tilename(origin_tile)
      assert origin_gridinfo.tile_type == 'CLBLL_R'
      origin_site = sorted(origin_gridinfo.sites.keys())[random.randint(0, 1)]
  
      dest_tile = grid.tilename_at_loc((loc.grid_x + 4, loc.grid_y))
      dest_gridinfo = grid.gridinfo_at_tilename(dest_tile)
      assert dest_gridinfo.tile_type == 'CLBLL_L'
      dest_site = sorted(dest_gridinfo.sites.keys())[0]
  
      dest2_tile = grid.tilename_at_loc((loc.grid_x + 4, loc.grid_y + 1))
      dest2_gridinfo = grid.gridinfo_at_tilename(dest2_tile)
      assert dest2_gridinfo.tile_type == 'CLBLL_L', dest2_gridinfo.tile_type
      dest2_site = sorted(dest2_gridinfo.sites.keys())[1]
  
      if random.randint(0, 1):
          dest_wire = 'origin_wire_{origin_site}'.format(origin_site=origin_site)
      else:
          dest_wire = '1'
  
      if random.randint(0, 1):
          dest_wire2 = 'origin_wire_{origin_site}'.format(
              origin_site=origin_site)
      else:
          dest_wire2 = '1'
  
      if random.randint(0, 1):
          dest_wire3 = 'origin_wire_{origin_site}'.format(
              origin_site=origin_site)
      else:
          dest_wire3 = '1'
  
      if random.randint(0, 1):
          dest_wire4 = 'origin_wire_{origin_site}'.format(
              origin_site=origin_site)
      else:
          dest_wire4 = '1'
  
      # origin_site.AQ -> dest_tile.D6 enables INT_R.ER1BEG1.EE4END0
      print(
          """
      // Force origin FF into A position with MUXF7_L and MUXF8
      wire origin_wire_{origin_site};
  
      wire f7_to_lo_{origin_site};
      wire lut_to_f7_{origin_site};
      (* KEEP, DONT_TOUCH, LOC = "{origin_site}" *)
      LUT6_L #(
      .INIT(0)
      ) lut_rom_{origin_site} (
              .I0(1),
              .I1(origin_wire_{origin_site}),
              .I2(0),
              .I3(1),
              .I4(1),
              .I5(1),
              .LO(lut_to_f7_{origin_site})
              );
      (* KEEP, DONT_TOUCH, LOC = "{origin_site}" *)
      MUXF7_D f7_{origin_site} (
          .I0(lut_to_f7_{origin_site}),
          .LO(f7_to_lo_{origin_site}),
          .O(origin_wire_{origin_site})
          );
      (* KEEP, DONT_TOUCH, LOC = "{origin_site}" *)
      MUXF8 f8_{origin_site} (
          .I1(f7_to_lo_{origin_site})
          );
  
      wire dest_wire_{dest_site};
      wire dest_wire2_{dest_site};
  
      wire d_lut_to_f7_{dest_site}, f7_to_f8_{dest_site};
      // Force destination LUT into D position with MUXF7_L and MUXF8
      (* KEEP, DONT_TOUCH, LOC = "{dest_site}" *)
      LUT6_L #(
      .INIT(0)
      ) d_lut_rom_{dest_site} (
              .I0(1),
              .I1(1),
              .I2(0),
              .I3(1),
              .I4(1),
              .I5(dest_wire_{dest_site}),
              .LO(d_lut_to_f7_{dest_site})
              );
  
      wire c_lut_to_f7_{dest_site};
      // Force destination LUT into C position with MUXF7_L and MUXF8
      (* KEEP, DONT_TOUCH, LOC = "{dest_site}" *)
      LUT6_L #(
      .INIT(0)
      ) c_lut_rom_{dest_site} (
              .I0(1),
              .I1(1),
              .I2(0),
              .I3(1),
              .I4(1),
              .I5(dest_wire2_{dest_site}),
              .LO(c_lut_to_f7_{dest_site})
              );
      (* KEEP, DONT_TOUCH, LOC = "{dest_site}" *)
      MUXF7_L f7_{dest_site} (
          .I0(d_lut_to_f7_{dest_site}),
          .I1(c_lut_to_f7_{dest_site}),
          .LO(f7_to_f8_{dest_site})
          );
      (* KEEP, DONT_TOUCH, LOC = "{dest_site}" *)
      MUXF8 f8_{dest_site} (
          .I0(f7_to_f8_{dest_site})
          );
  
      assign dest_wire_{dest_site} = {dest_wire};
      assign dest_wire2_{dest_site} = {dest_wire2};
  
      wire dest_wire3_{dest_site2};
      wire dest_wire4_{dest_site2};
      wire lut_to_f7_{dest_site2}, f7_to_f8_{dest_site2};
      // Force destination LUT into D position with MUXF7_L and MUXF8
      (* KEEP, DONT_TOUCH, LOC = "{dest_site2}" *)
      LUT6_L #(
      .INIT(0)
      ) d_lut_rom_{dest_site2} (
              .I0(dest_wire3_{dest_site2}),
              .I1(1),
              .I2(0),
              .I3(1),
              .I4(1),
              .I5(),
              .LO(lut_to_f7_{dest_site2})
              );
  
      // Force destination LUT into C position with MUXF7_L and MUXF8
      wire c_lut_to_f7_{dest_site2};
      (* KEEP, DONT_TOUCH, LOC = "{dest_site2}" *)
      LUT6_L #(
      .INIT(0)
      ) c_lut_rom_{dest_site2} (
              .I0(dest_wire4_{dest_site2}),
              .I1(1),
              .I2(0),
              .I3(1),
              .I4(1),
              .I5(1),
              .LO(c_lut_to_f7_{dest_site2})
              );
      (* KEEP, DONT_TOUCH, LOC = "{dest_site2}" *)
      MUXF7_L f7_{dest_site2} (
          .I0(lut_to_f7_{dest_site2}),
          .I1(c_lut_to_f7_{dest_site2}),
          .LO(f7_to_f8_{dest_site2})
          );
      (* KEEP, DONT_TOUCH, LOC = "{dest_site2}" *)
      MUXF8 f8_{dest_site2} (
          .I0(f7_to_f8_{dest_site2})
          );
  
      assign dest_wire3_{dest_site2} = {dest_wire3};
      assign dest_wire4_{dest_site2} = {dest_wire4};
      """.format(
              dest_site=dest_site,
              dest_site2=dest2_site,
              origin_site=origin_site,
              dest_wire=dest_wire,
              dest_wire2=dest_wire2,
              dest_wire3=dest_wire3,
              dest_wire4=dest_wire4,
          ))
  
  
  def run():
      print('''
  module top();
      ''')
  
      int_tiles = []
  
      db = Database(util.get_db_root(), util.get_part())
      grid = db.grid()
      params = {}
      for int_l_column, int_r_column in gen_orphan_ints(grid):
          for int_l, int_r in zip(int_l_column[1:6:2], int_r_column[1:6:2]):
              build_cross_int(params, grid, int_l, int_r)
              int_tiles.append(int_l)
  
      print("endmodule")
      with open('top.txt', 'w') as f:
          for tile in int_tiles:
              print(tile, file=f)
  
  
  if __name__ == '__main__':
      run()
  