import os
import subprocess
import unittest

from amaranth.build import *
from amaranth.vendor import LatticeECP5Platform
from .resources import *


__all__ = ["VersaECP5Platform"]


class VersaECP5Platform(LatticeECP5Platform):
    device      = "LFE5UM-45F"
    package     = "BG381"
    speed       = "8"
    default_clk = "clk100"
    default_rst = "rst"
    resources   = [
        Resource("rst", 0, PinsN("T1", dir="i"), Attrs(IO_TYPE="LVCMOS33")),
        Resource("clk100", 0, DiffPairs("P3", "P4", dir="i"),
                 Clock(100e6), Attrs(IO_TYPE="LVDS")),
        Resource("pclk", 0, DiffPairs("A4", "A5", dir="i"),
                 Attrs(IO_TYPE="LVDS")),

        *LEDResources(pins="E16 D17 D18 E18 F17 F18 E17 F16", invert=True,
                      attrs=Attrs(IO_TYPE="LVCMOS25")),

        Resource("alnum_led", 0,
            Subsignal("a", PinsN("M20", dir="o")),
            Subsignal("b", PinsN("L18", dir="o")),
            Subsignal("c", PinsN("M19", dir="o")),
            Subsignal("d", PinsN("L16", dir="o")),
            Subsignal("e", PinsN("L17", dir="o")),
            Subsignal("f", PinsN("M18", dir="o")),
            Subsignal("g", PinsN("N16", dir="o")),
            Subsignal("h", PinsN("M17", dir="o")),
            Subsignal("j", PinsN("N18", dir="o")),
            Subsignal("k", PinsN("P17", dir="o")),
            Subsignal("l", PinsN("N17", dir="o")),
            Subsignal("m", PinsN("P16", dir="o")),
            Subsignal("n", PinsN("R16", dir="o")),
            Subsignal("p", PinsN("R17", dir="o")),
            Subsignal("dp", PinsN("U1", dir="o")),
            Attrs(IO_TYPE="LVCMOS25")
        ),

        *SwitchResources(pins={0: "H2",  1: "K3",  2: "G3",  3: "F2" },
                         attrs=Attrs(IO_TYPE="LVCMOS15")),
        *SwitchResources(pins={4: "J18", 5: "K18", 6: "K19", 7: "K20"},
                         attrs=Attrs(IO_TYPE="LVCMOS25")),

        UARTResource(0,
            rx="C11", tx="A11",
            attrs=Attrs(IO_TYPE="LVCMOS33", PULLMODE="UP")
        ),

        *SPIFlashResources(0,
            cs_n="R2", clk="U3", cipo="W2", copi="V2", wp_n="Y2", hold_n="W1",
            attrs=Attrs(IO_TYPE="LVCMOS33")
        ),

        Resource("eth_clk125",     0, Pins("L19", dir="i"),
                 Clock(125e6), Attrs(IO_TYPE="LVCMOS25")),
        Resource("eth_clk125_pll", 0, Pins("U16", dir="i"),
                 Clock(125e6), Attrs(IO_TYPE="LVCMOS25")), # NC by default
        Resource("eth_rgmii", 0,
            Subsignal("rst",     PinsN("U17", dir="o")),
            Subsignal("mdc",     Pins("T18", dir="o")),
            Subsignal("mdio",    Pins("U18", dir="io")),
            Subsignal("tx_clk",  Pins("P19", dir="o")),
            Subsignal("tx_ctl",  Pins("R20", dir="o")),
            Subsignal("tx_data", Pins("N19 N20 P18 P20", dir="o")),
            Subsignal("rx_clk",  Pins("L20", dir="i")),
            Subsignal("rx_ctl",  Pins("U19", dir="i")),
            Subsignal("rx_data", Pins("T20 U20 T19 R18", dir="i")),
            Attrs(IO_TYPE="LVCMOS25")
        ),
        Resource("eth_sgmii", 0,
            Subsignal("rst",     PinsN("U17", dir="o"), Attrs(IO_TYPE="LVCMOS25")),
            Subsignal("mdc",     Pins("T18", dir="o"), Attrs(IO_TYPE="LVCMOS25")),
            Subsignal("mdio",    Pins("U18", dir="io"), Attrs(IO_TYPE="LVCMOS25")),
            Subsignal("tx",      DiffPairs("W13", "W14", dir="o")),
            Subsignal("rx",      DiffPairs("Y14", "Y15", dir="i")),
        ),

        Resource("eth_clk125",     1, Pins("J20", dir="i"),
                 Clock(125e6), Attrs(IO_TYPE="LVCMOS25")),
        Resource("eth_clk125_pll", 1, Pins("C18", dir="i"),
                 Clock(125e6), Attrs(IO_TYPE="LVCMOS25")), # NC by default
        Resource("eth_rgmii", 1,
            Subsignal("rst",     PinsN("F20", dir="o")),
            Subsignal("mdc",     Pins("G19", dir="o")),
            Subsignal("mdio",    Pins("H20", dir="io")),
            Subsignal("tx_clk",  Pins("C20", dir="o")),
            Subsignal("tx_ctrl", Pins("E19", dir="o")),
            Subsignal("tx_data", Pins("J17 J16 D19 D20", dir="o")),
            Subsignal("rx_clk",  Pins("J19", dir="i")),
            Subsignal("rx_ctrl", Pins("F19", dir="i")),
            Subsignal("rx_data", Pins("G18 G16 H18 H17", dir="i")),
            Attrs(IO_TYPE="LVCMOS25")
        ),
        Resource("eth_sgmii", 1,
            Subsignal("rst",     PinsN("F20", dir="o"), Attrs(IO_TYPE="LVCMOS25")),
            Subsignal("mdc",     Pins("G19", dir="o"), Attrs(IO_TYPE="LVCMOS25")),
            Subsignal("mdio",    Pins("H20", dir="io"), Attrs(IO_TYPE="LVCMOS25")),
            Subsignal("tx",      DiffPairs("W17", "W18", dir="o")),
            Subsignal("rx",      DiffPairs("Y16", "Y17", dir="i")),
        ),

        Resource("ddr3", 0,
            Subsignal("rst",     PinsN("N4", dir="o")),
            Subsignal("clk",     DiffPairs("M4", "N5", dir="o"), Attrs(IO_TYPE="SSTL135D_I")),
            Subsignal("clk_en",  Pins("N2", dir="o")),
            Subsignal("cs",      PinsN("K1", dir="o")),
            Subsignal("we",      PinsN("M1", dir="o")),
            Subsignal("ras",     PinsN("P1", dir="o")),
            Subsignal("cas",     PinsN("L1", dir="o")),
            Subsignal("a",       Pins("P2 C4 E5 F5 B3 F4 B5 E4 C5 E3 D5 B4 C3", dir="o")),
            Subsignal("ba",      Pins("P5 N3 M3", dir="o")),
            Subsignal("dqs",     DiffPairs("K2 H4", "J1 G5", dir="io"), Attrs(IO_TYPE="SSTL135D_I", DIFFRESISTOR="100", TERMINATION="OFF")),
            Subsignal("dq",      Pins("L5 F1 K4 G1 L4 H1 G2 J3 D1 C1 E2 C2 F3 A2 E1 B1",
                                      dir="io")), Attrs(TERMINATION="75"),
            Subsignal("dm",      Pins("J4 H5", dir="o")),
            Subsignal("odt",     Pins("L2", dir="o")),
            Attrs(IO_TYPE="SSTL135_I", SLEWRATE="FAST")
        )
    ]
    connectors = [
        Connector("expcon", 1, """
        -   -   -   B19 B12 B9  E6  D6  E7  D7  B11 B6  E9  D9  B8  C8  D8  E8  C7  C6
        -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -
        """), # X3
        Connector("expcon", 2, """
        A8  -   A12 A13 B13 C13 D13 E13 A14 C14 D14 E14 D11 C10 A9  B10 D12 E12 -   -
        B15 -   C15 -   D15 -   E15 A16 B16 -   C16 D16 B17 -   C17 A17 B18 A7  A18 -
        """), # X4
    ]

    @property
    def file_templates(self):
        return {
            **super().file_templates,
            "{{name}}-openocd.cfg": r"""
            adapter driver ftdi
            adapter speed 25000
            {# FTDI descriptors is identical between non-5G and 5G recent Versa boards #}
            ftdi device_desc "Lattice ECP5_5G VERSA Board"
            ftdi vid_pid 0x0403 0x6010
            ftdi channel 0
            ftdi layout_init 0xfff8 0xfffb
            reset_config none

            # ispCLOCK device (unusable with openocd and must be bypassed)
            #jtag newtap ispclock tap -irlen 8 -expected-id 0x00191043
            # ECP5 device
            {% if "5G" in platform.device -%}
            jtag newtap ecp5 tap -irlen 8 -expected-id 0x81112043 ; # LFE5UM5G-45F
            {% else -%}
            jtag newtap ecp5 tap -irlen 8 -expected-id 0x01112043 ; # LFE5UM-45F
            {% endif %}
            """
        }

    def toolchain_program(self, products, name):
        openocd = os.environ.get("OPENOCD", "openocd")
        with products.extract("{}-openocd.cfg".format(name), "{}.svf".format(name)) \
                as (config_filename, vector_filename):
            subprocess.check_call([openocd,
                "-f", config_filename,
                "-c", "transport select jtag; init; svf -quiet {}; exit".format(vector_filename)
            ])


class TestCase(unittest.TestCase):
    def test_smoke(self):
        from .test.blinky import Blinky
        VersaECP5Platform().build(Blinky(), do_build=False)


if __name__ == "__main__":
    from .test.blinky import Blinky
    VersaECP5Platform().build(Blinky(), do_program=True)
