from amaranth import *
from amaranth.lib.io import pin_layout
from amaranth_soc.wishbone.bus import Interface
from amaranth_soc.memory import MemoryMap


__all__ = ["LiteEth", "rgmii_layout"]


# TODO maybe this should just call liteeth_gen to close the loop?
class LiteEth(Elaboratable, Interface):
    def __init__(self, eth_interface):
        self.eth_interface = eth_interface

        # Addr width is 13 bits to accomodate 0x1FFF, which is well past what we care about
        Interface.__init__(self, addr_width=15, data_width=32, granularity=8, features=["cti", "bte", "err"])
        # TODO I need to understand the semantics here better
        memory_map = MemoryMap(addr_width=17, data_width=8)
        #memory_map.add_resource(self, name="LiteETH", size=0x2000)
        self.memory_map = memory_map

        self.interrupt = Signal()


    # TODO this really shouldn't technically happen here, because we can elaborate one module multiple times,
    # but since I use it once it isn't actually a problem.
    def elaborate(self, platform):
        if platform is not None:
            platform.add_file("liteeth_core.v", open("liteeth/gateware/liteeth_core.v", 'r').read())

        m = Module()


        # TODO I have to provide TX/RX clocks myself
        core = Instance(
            "liteeth_core",
            i_sys_clock=ClockSignal(),
            i_sys_reset=Const(0),

            # RGMII signals
            o_rgmii_eth_clocks_tx=self.eth_interface.tx_clk,
            i_rgmii_eth_clocks_rx=self.eth_interface.rx_clk,
            o_rgmii_eth_rst_n=self.eth_interface.rst,
            i_rgmii_eth_int_n=Const(1),

            # Bad hacks
            o_rgmii_eth_mdio_o=self.eth_interface.mdio.o,
            o_rgmii_eth_mdio_oe=self.eth_interface.mdio.oe,
            i_rgmii_eth_mdio_i=self.eth_interface.mdio.i,

            o_rgmii_eth_mdc=self.eth_interface.mdc,
            i_rgmii_eth_rx_ctl=self.eth_interface.rx_ctl,
            i_rgmii_eth_rx_data=self.eth_interface.rx_data,
            o_rgmii_eth_tx_ctl=self.eth_interface.tx_ctl,
            o_rgmii_eth_tx_data=self.eth_interface.tx_data,

            # Wishbone all the things
            i_wishbone_adr=self.adr,
            i_wishbone_dat_w=self.dat_w,
            o_wishbone_dat_r=self.dat_r,
            i_wishbone_sel=self.sel,
            i_wishbone_cyc=self.cyc,
            i_wishbone_stb=self.stb,
            o_wishbone_ack=self.ack,
            i_wishbone_we=self.we,
            i_wishbone_cti=self.cti,
            i_wishbone_bte=self.bte,
            o_wishbone_err=self.err,

            o_interrupt=self.interrupt,
        )

        m.submodules.core = core

        return m


rgmii_layout = [
    ("tx_clk", pin_layout(1, "o")),
    ("rx_clk", pin_layout(1, "i")),
    ("rst", pin_layout(1, "o")),
    ("int_n", pin_layout(1, "i")),

    # TODO is this not IO? why does LiteEth say input?
    # I think the answer is it uses a primitive, not 100% right now
    ("mdio", pin_layout(1, "io")),
    ("mdc", pin_layout(1, "o")),
    ("rx_ctl", pin_layout(1, "i")),
    ("rx_data", pin_layout(4, "i")),
    ("tx_ctl", pin_layout(1, "o")),
    ("tx_data", pin_layout(4, "o")),
]

