import pytrellis
import nonrouting
from fuzzconfig import FuzzConfig
import fuzzloops

"""
This fuzzer is ***HORRIBLE***, but the interfaces we have access to mean there is no better option.

CIB interconnect tiles have muxes that allow the connections into special function tiles from the CIB can be driven to
constant 0 or 1, as well as from general routing. But there is no way to specify this directly for the CIB connections
(which are named as LUT inputs [A-D][0-7]). So this horrible hack emerges instead, whereby we trace the CIB->EBR 
interconnect and drive constants on the EBR instead. We can't treat this as just an EBR configuration as it applies to
all CIBs, including those where this would be difficult to fuzz and location-dependant. So there's probably not much 
choice...
"""

# List of all pins to fuzz
fuzz_pins = [
    "JA0", "JA1", "JA2", "JA3", "JA4", "JA5", "JA6", "JA7",
    "JB0", "JB1", "JB2", "JB3", "JB4", "JB5", "JB6", "JB7",
    "JC0", "JC1", "JC2", "JC3", "JC4", "JC5", "JC6", "JC7",
    "JD0", "JD1", "JD2", "JD3", "JD4", "JD5", "JD6", "JD7",
    "JCE0", "JCE1", "JCE2", "JCE3", "JCLK0", "JCLK1", "JLSR0", "JLSR1"
]

cfg = FuzzConfig(job="CIBCONST", family="ECP5", device="LFE5U-25F", ncl="empty.ncl",
                 tiles=["CIB_R25C26:CIB_EBR", "CIB_R25C27:CIB_EBR", "CIB_R25C28:CIB_EBR"])


def main():
    pytrellis.load_database("../../../database")
    cfg.setup()
    empty_bitfile = cfg.build_design(cfg.ncl, {})
    cfg.ncl = "cibconst.ncl"

    def per_pin(pin):
        def get_substs(sig, val):
            if val == pin:
                val = "#SIG"
            subs = {"sig": sig, "val": val}
            return subs

        if pin.startswith("JCE"):
            options = [pin, "1"]
        elif pin.startswith("JCLK") or pin.startswith("JLSR"):
            options = [pin, "0"]
        else:
            options = [pin, "0", "1"]
        # Load the EBR database and find the correct signal
        ebrdb = pytrellis.get_tile_bitdata(
            pytrellis.TileLocator(cfg.family, cfg.device, "MIB_EBR4"))
        fconns = ebrdb.get_fixed_conns()
        sig = None
        for conn in fconns:
            if conn.source.endswith(pin):
                sig = conn.sink
                break
        assert sig is not None
        # Convert net name to NCL pin name by stripping extraneous content
        if sig[0] == "J":
            sig = sig[1:]
        sig = sig.replace("_EBR", "")

        nonrouting.fuzz_enum_setting(cfg, "CIB.{}MUX".format(pin), options,
                                     lambda x: get_substs(sig=sig, val=x),
                                     empty_bitfile, False)

    fuzzloops.parallel_foreach(fuzz_pins, per_pin)


if __name__ == "__main__":
    main()
