#!/usr/bin/env python3

from migen import *

from litex.gen import *

from litex.soc.cores.clock import CycloneIVPLL
from litex.soc.integration.soc_core import *
from litex.soc.integration.builder import *
from litex.soc.interconnect.csr import *
from migen.genlib.resetsync import AsyncResetSynchronizer

import mmuless_board

# CRG --------------------------------------------------------------------------

class _CRG(LiteXModule):
    def __init__(self, platform, sys_clk_freq):
        self.rst     = Signal()
        self.cd_por  = ClockDomain(reset_less=True)
        self.cd_sys  = ClockDomain()
        # clock domain used only for reset button sync
        self.cd_sync = ClockDomain()

        clk50 = platform.request("clk50")
        rst_n = platform.request("rst_n")

        # POR
        por_count = Signal(16, reset=2**16 - 1)
        por_done  = Signal()
        self.comb += self.cd_por.clk.eq(clk50)
        self.comb += por_done.eq(por_count == 0)
        self.sync.por += If(~por_done, por_count.eq(por_count - 1))

        # PLL
        self.pll = pll = CycloneIVPLL(speedgrade="-6")
        self.comb += pll.reset.eq(~por_done | self.rst)
        pll.register_clkin(clk50, 50e6)
        pll.create_clkout(self.cd_sys, sys_clk_freq, margin=0, with_reset=False)
        
        # Sys CD
        sys_rst = Signal()
        self.comb += sys_rst.eq(~pll.locked)
        self.specials += AsyncResetSynchronizer(self.cd_sys, sys_rst)

        # Sync Cd
        self.comb += self.cd_sync.clk.eq(self.cd_sys.clk)
        self.specials += AsyncResetSynchronizer(self.cd_sync, ~rst_n)


# BaseSoC ----------------------------------------------------------------------

class BaseSoC(SoCCore):
    def __init__(self, sys_clk_freq=50e6,
        **kwargs):

        platform = mmuless_board.Platform()

        # CRG ------------------------------------------------------------------
        self.crg = _CRG(platform, sys_clk_freq)

        # SoCCore --------------------------------------------------------------
        if kwargs["with_jtagbone"]:
            if kwargs.get("uart_name", "serial") == "serial": kwargs["uart_name"] = "crossover"

        SoCCore.__init__(self, platform, sys_clk_freq,
            ident = "LiteX SoC on SiMiaoHub AC440B",
            **kwargs
        )

        # Leds -----------------------------------------------------------------
        leds = Cat(*[platform.request("u_led_n", i) for i in range(4)])
        self.submodules.leds = CSRStorage(len(leds), description="Board LEDs")
        self.comb += leds.eq(~self.leds.storage)

        # Buttons --------------------------------------------------------------
        keys = Cat(*[platform.request("u_key_n", i) for i in range(2)])
        self.submodules.btns = CSRStatus(len(keys), description="Board buttons")
        self.comb += self.btns.status.eq(~keys)
        # Reset button hook
        self.comb += If(self.crg.cd_sync.rst, self.cpu.reset.eq(1))



# Build ------------------------------------------------------------------------

def main():
    from litex.build.parser import LiteXArgumentParser
    parser = LiteXArgumentParser(platform=mmuless_board.Platform, description="LiteX SoC on SiMiaoHub AC440B")
    parser.add_target_argument("--sys-clk-freq", default=50e6, type=float, help="System clock frequency.")
    args = parser.parse_args()
    
    soc = BaseSoC(
        sys_clk_freq = args.sys_clk_freq,
        **parser.soc_argdict
    )

    builder = Builder(soc, **parser.builder_argdict)
    if args.build:
        builder.build(**parser.toolchain_argdict)

    if args.load:
        prog = soc.platform.create_programmer()
        prog.load_bitstream(builder.get_bitstream_filename(mode="sram"))

if __name__ == "__main__":
    main()
