#!/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
'''
Generate a primitive to place at every I/O
Unlike CLB tests, the LFSR for this is inside the ROI, not driving it
'''

import os
import random
random.seed(int(os.getenv("SEED"), 16))
from prjxray import util
from prjxray.db import Database
import re


def gen_sites():
    '''
    IOB33S: main IOB of a diff pair
    IOB33M: secondary IOB of a diff pair
    IOB33: not a diff pair. Relatively rare (at least in ROI...2 of them?)
    Focus on IOB33S to start
    '''
    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)

        sites = []
        for site_name, site_type in gridinfo.sites.items():
            if site_type == 'IDELAYE2':
                sites.append(site_name)

        if len(sites) == 0:
            continue

        sites_y = [
            int(re.match('IDELAY_X[0-9]+Y([0-9]+)', site).group(1))
            for site in sites
        ]

        sites, _ = zip(*sorted(zip(sites, sites_y), key=lambda x: x[1]))

        if gridinfo.tile_type[0] == 'L':
            int_grid_x = loc.grid_x + 3
            pad_grid_x = loc.grid_x - 1
            int_tile_type = 'INT_L'
        else:
            int_grid_x = loc.grid_x - 3
            pad_grid_x = loc.grid_x + 1
            int_tile_type = 'INT_R'

        int_tile_locs = [
            (int_grid_x, loc.grid_y),
        ]

        pad_gridinfo = grid.gridinfo_at_loc((pad_grid_x, loc.grid_y))

        pad_sites = pad_gridinfo.sites.keys()
        pad_sites_y = [
            int(re.match('IOB_X[0-9]+Y([0-9]+)', site).group(1))
            for site in pad_sites
        ]
        pad_sites, _ = zip(
            *sorted(zip(pad_sites, pad_sites_y), key=lambda x: x[1]))

        if not gridinfo.tile_type.endswith("_SING"):
            int_tile_locs.append((int_grid_x, loc.grid_y - 1))

        assert len(sites) == len(int_tile_locs), (
            tile_name, sites, int_tile_locs)
        assert len(sites) == len(pad_sites), (sites, pad_sites)

        for site_name, pad_site, int_tile_loc in zip(sites, pad_sites,
                                                     int_tile_locs):
            int_tile_name = grid.tilename_at_loc(int_tile_loc)
            assert int_tile_name.startswith(int_tile_type), (
                int_tile_name, site_name, int_tile_loc)
            yield int_tile_name, site_name, pad_site


def write_params(params):
    pinstr = ''
    for tile, (site, val, pad_site_name, pin) in sorted(params.items()):
        pinstr += '%s,%s,%s,%s,%s\n' % (tile, val, site, pad_site_name, pin)
    open('params.csv', 'w').write(pinstr)


def run():
    sites = list(gen_sites())
    print(
        '''
`define N_DI {}

module top(input wire [`N_DI-1:0] di);
    wire [`N_DI-1:0] di_buf;

    (* KEEP, DONT_TOUCH, IODELAY_GROUP = "iodelays" *)
    IDELAYCTRL idelayctrl (
        .REFCLK()
    );
    '''.format(len(sites)))

    params = {}

    for idx, ((tile_name, site_name, pad_site_name), isone) in enumerate(zip(
            sites, util.gen_fuzz_states(len(sites)))):
        params[tile_name] = (site_name, isone, pad_site_name, "di[%u]" % idx)

        # Force HARD0 -> GFAN1 with CNTVALUEIN4 = 0
        # Toggle 1 pip with CNTVALUEIN3 = ?
        print(
            '''

            // Solving for {3}
            (* KEEP, DONT_TOUCH *)
            IBUF ibuf_{0}(.I(di[{2}]), .O(di_buf[{2}]));

            (* KEEP, DONT_TOUCH, LOC = "{0}", IODELAY_GROUP = "iodelays" *)
            IDELAYE2 idelay_{0} (
                .CNTVALUEIN(5'b0{1}111),
                .IDATAIN(di_buf[{2}])
                );
'''.format(site_name, isone, idx, tile_name))

    print("endmodule")
    write_params(params)


if __name__ == '__main__':
    run()
