from amaranth            import *
from amaranth.build      import *
from amaranth.lib.cdc    import ResetSynchronizer
from amaranth.lib.wiring import flipped

from amlib.utils import SimpleClockDivider


class ClockDomainGeneratorBase():
    NO_PHASE_SHIFT  = 0

    def wire_up_reset(self, m, reset):
        m.submodules.reset_sync_fast = ResetSynchronizer(reset, domain="fast")
        m.submodules.reset_sync_usb  = ResetSynchronizer(reset, domain="usb")
        m.submodules.reset_sync_sync = ResetSynchronizer(reset, domain="sync")
        m.submodules.reset_sync_dac  = ResetSynchronizer(reset, domain="dac")
        m.submodules.reset_sync_adat = ResetSynchronizer(reset, domain="adat")

class IntelCycloneIVClockDomainGenerator(Elaboratable, ClockDomainGeneratorBase):
    ADAT_DIV_48k    = 83
    ADAT_MULT_48k   = 17

    ADAT_DIV_44_1k  = 62
    ADAT_MULT_44_1k = 14

    DUTY_CYCLE      = 50

    def __init__(self, *, clock_frequencies=None, clock_signal_name=None):
        pass

    def elaborate(self, platform):
        m = Module()

        # Create our domains
        m.domains.usb  = ClockDomain("usb")
        m.domains.sync = ClockDomain("sync")
        m.domains.fast = ClockDomain("fast")
        m.domains.adat = ClockDomain("adat")
        m.domains.dac  = ClockDomain("dac")

        clk = flipped(platform.request(platform.default_clk))

        main_clocks    = Signal(5)
        audio_clocks   = Signal(4)
        fast_clock_48k = Signal()

        sys_locked   = Signal()
        audio_locked = Signal()
        fast_locked  = Signal()
        reset        = Signal()

        m.submodules.mainpll = Instance("ALTPLL",
            p_BANDWIDTH_TYPE         = "AUTO",

            # USB clock: 60MHz
            p_CLK0_DIVIDE_BY         = 5,
            p_CLK0_DUTY_CYCLE        = self.DUTY_CYCLE,
            p_CLK0_MULTIPLY_BY       = 6,
            p_CLK0_PHASE_SHIFT       = self.NO_PHASE_SHIFT,

            # 44.1k ADAT Clock 11.2896 MHz = 44.1kHz * 256
            p_CLK1_DIVIDE_BY         = self.ADAT_DIV_44_1k,
            p_CLK1_DUTY_CYCLE        = self.DUTY_CYCLE,
            p_CLK1_MULTIPLY_BY       = self.ADAT_MULT_44_1k,
            p_CLK1_PHASE_SHIFT       = self.NO_PHASE_SHIFT,

            # I2S DAC clock 44.1k = 3.072 MHz = 44.1kHz * 32 bit * 2 channels
            p_CLK2_DIVIDE_BY         = self.ADAT_DIV_44_1k * 4,
            p_CLK2_DUTY_CYCLE        = self.DUTY_CYCLE,
            p_CLK2_MULTIPLY_BY       = self.ADAT_MULT_44_1k,
            p_CLK2_PHASE_SHIFT       = self.NO_PHASE_SHIFT,

            # ADAT sampling clock = 44.1kHz * 256 * 8 times oversampling
            p_CLK3_DIVIDE_BY         = self.ADAT_DIV_44_1k,
            p_CLK3_DUTY_CYCLE        = self.DUTY_CYCLE,
            p_CLK3_MULTIPLY_BY       = self.ADAT_MULT_44_1k * 8,
            p_CLK3_PHASE_SHIFT       = self.NO_PHASE_SHIFT,

            # ADAT transmit domain clock = 44.1kHz * 5 output terminals
            p_CLK4_DIVIDE_BY         = self.ADAT_DIV_44_1k,
            p_CLK4_DUTY_CYCLE        = self.DUTY_CYCLE,
            p_CLK4_MULTIPLY_BY       = self.ADAT_MULT_44_1k * 5,
            p_CLK4_PHASE_SHIFT       = self.NO_PHASE_SHIFT,

            # 50MHz = 20000 picoseconds
            p_INCLK0_INPUT_FREQUENCY = 20000,
            p_OPERATION_MODE         = "NORMAL",

            # Drive our clock from the USB clock
            # coming from the USB clock pin of the USB3300
            i_inclk  = clk,
            o_clk    = main_clocks,
            o_locked = sys_locked,
        )

        m.submodules.audiopll = Instance("ALTPLL",
            p_BANDWIDTH_TYPE         = "AUTO",

            # ADAT clock = 12.288 MHz = 48 kHz * 256
            p_CLK0_DIVIDE_BY         = self.ADAT_DIV_48k,
            p_CLK0_DUTY_CYCLE        = self.DUTY_CYCLE,
            p_CLK0_MULTIPLY_BY       = self.ADAT_MULT_48k,
            p_CLK0_PHASE_SHIFT       = self.NO_PHASE_SHIFT,

            # I2S DAC clock 48k = 3.072 MHz = 48 kHz * 32 bit * 2 channels
            p_CLK1_DIVIDE_BY         = self.ADAT_DIV_48k * 4,
            p_CLK1_DUTY_CYCLE        = self.DUTY_CYCLE,
            p_CLK1_MULTIPLY_BY       = self.ADAT_MULT_48k,
            p_CLK1_PHASE_SHIFT       = self.NO_PHASE_SHIFT,

            # ADAT sampling clock = 48 kHz * 256 * 8 times oversampling
            p_CLK2_DIVIDE_BY         = self.ADAT_DIV_48k,
            p_CLK2_DUTY_CYCLE        = self.DUTY_CYCLE,
            p_CLK2_MULTIPLY_BY       = self.ADAT_MULT_48k * 8,
            p_CLK2_PHASE_SHIFT       = self.NO_PHASE_SHIFT,

            # ADAT transmit domain clock = 48 kHz * 256 * 5 output terminals
            p_CLK3_DIVIDE_BY         = self.ADAT_DIV_48k,
            p_CLK3_DUTY_CYCLE        = self.DUTY_CYCLE,
            p_CLK3_MULTIPLY_BY       = self.ADAT_MULT_48k * 5,
            p_CLK3_PHASE_SHIFT       = self.NO_PHASE_SHIFT,

            p_INCLK0_INPUT_FREQUENCY = 16667,
            p_OPERATION_MODE         = "NORMAL",

            i_inclk  = main_clocks[0],
            o_clk    = audio_clocks,
            o_locked = audio_locked,
        )

        m.submodules.fastpll = Instance("ALTPLL",
            p_BANDWIDTH_TYPE         = "AUTO",

            # ADAT sampling clock = 48 kHz * 256 * 8 times oversampling
            p_CLK0_DIVIDE_BY         = 1,
            p_CLK0_DUTY_CYCLE        = self.DUTY_CYCLE,
            p_CLK0_MULTIPLY_BY       = platform.fast_multiplier,
            p_CLK0_PHASE_SHIFT       = self.NO_PHASE_SHIFT,

            p_INCLK0_INPUT_FREQUENCY = 81395,
            p_OPERATION_MODE         = "NORMAL",

            i_inclk  = audio_clocks[0],
            o_clk    = fast_clock_48k,
            o_locked = fast_locked,
        )


        m.d.comb += [
            reset.eq(~(sys_locked & audio_locked & fast_locked)),
            ClockSignal("fast").eq(fast_clock_48k),
            ClockSignal("usb") .eq(main_clocks[0]),
            ClockSignal("adat").eq(audio_clocks[0]),
            ClockSignal("dac").eq(audio_clocks[1]),
            ClockSignal("sync").eq(audio_clocks[3]),
        ]

        self.wire_up_reset(m, reset)

        return m


class IntelCycloneVClockDomainGenerator(Elaboratable, ClockDomainGeneratorBase):

    def __init__(self, *, clock_frequencies=None, clock_signal_name=None):
        pass

    def elaborate(self, platform):
        m = Module()

        # Create our domains
        # usb: USB clock: 60MHz
        # adat: ADAT clock = 12.288 MHz = 48 kHz * 256
        # dac: I2S DAC clock 48k = 3.072 MHz = 48 kHz * 32 bit * 2 channels
        # sync: ADAT transmit domain clock = 61.44 MHz = 48 kHz * 256 * 5 output terminals
        # fast: ADAT sampling clock = 98.304 MHz = 48 kHz * 256 * 8 times oversampling
        m.domains.usb  = ClockDomain("usb")
        m.domains.sync = ClockDomain("sync")
        m.domains.fast = ClockDomain("fast")
        m.domains.adat = ClockDomain("adat")
        m.domains.dac  = ClockDomain("dac")

        clk = flipped(platform.request(platform.default_clk))

        main_clock    = Signal()
        audio_clocks  = Signal(4)

        sys_locked    = Signal()
        audio_locked  = Signal()

        reset         = Signal()

        m.submodules.mainpll = Instance("altera_pll",
            p_pll_type="General",
            p_pll_subtype="General",
            p_fractional_vco_multiplier="false",
            p_operation_mode="normal",
            p_reference_clock_frequency="50.0 MHz",
            p_number_of_clocks="1",

            p_output_clock_frequency0="60.000000 MHz",


            # Drive our clock from the internal 50MHz clock
            i_refclk = clk,
            o_outclk = main_clock,
            o_locked = sys_locked
        )

        m.submodules.audiopll = Instance("altera_pll",
            p_pll_type="General",
            p_pll_subtype="General",
            p_fractional_vco_multiplier="true",
            p_operation_mode="normal",
            p_reference_clock_frequency="60.0 MHz",
            p_number_of_clocks="4",

            p_output_clock_frequency0="12.288000 MHz",
            p_output_clock_frequency1="3.072000 MHz",
            p_output_clock_frequency2="61.440000 MHz",
            p_output_clock_frequency3="98.304000 MHz",

            # Drive our clock from the mainpll
            i_refclk=main_clock,
            o_outclk=audio_clocks,
            o_locked=audio_locked
        )

        m.d.comb += [
            reset.eq(~(sys_locked & audio_locked)),
            ClockSignal("usb") .eq(main_clock),
            ClockSignal("adat").eq(audio_clocks[0]),
            ClockSignal("dac").eq(audio_clocks[1]),
            ClockSignal("sync").eq(audio_clocks[2]),
            ClockSignal("fast").eq(audio_clocks[3])
        ]

        self.wire_up_reset(m, reset)

        return m

class Xilinx7SeriesClockDomainGenerator(Elaboratable, ClockDomainGeneratorBase):
    ADAT_DIV_48k    = 83
    ADAT_MULT_48k   = 17
    DUTY_CYCLE      = 0.5

    def __init__(self, *, clock_frequencies=None, clock_signal_name=None):
        pass

    def elaborate(self, platform):
        m = Module()

        # Create our domains
        m.domains.usb  = ClockDomain("usb")
        m.domains.sync = ClockDomain("sync")
        m.domains.fast = ClockDomain("fast")
        m.domains.adat = ClockDomain("adat")
        m.domains.dac  = ClockDomain("dac")

        clk = flipped(platform.request(platform.default_clk))

        main_clocks    = Signal()
        audio_clocks   = Signal(4)
        fast_clock_48k = Signal()

        sys_locked   = Signal()
        audio_locked = Signal()
        fast_locked  = Signal()
        reset        = Signal()

        mainpll_feedback  = Signal()
        audiopll_feedback = Signal()
        fastpll_feedback = Signal()

        m.submodules.mainpll = Instance("PLLE2_ADV",
            p_CLKIN1_PERIOD        = 20,
            p_BANDWIDTH            = "OPTIMIZED",
            p_COMPENSATION         = "ZHOLD",
            p_STARTUP_WAIT         = "FALSE",

            p_DIVCLK_DIVIDE        = 1,
            p_CLKFBOUT_MULT        = 30,
            p_CLKFBOUT_PHASE       = self.NO_PHASE_SHIFT,

            # 60MHz
            p_CLKOUT0_DIVIDE       = 25,
            p_CLKOUT0_PHASE        = self.NO_PHASE_SHIFT,
            p_CLKOUT0_DUTY_CYCLE   = self.DUTY_CYCLE,

            i_CLKFBIN              = mainpll_feedback,
            o_CLKFBOUT             = mainpll_feedback,
            i_CLKIN1               = clk,
            o_CLKOUT0              = main_clocks,
            o_LOCKED               = sys_locked,
        )

        m.submodules.audiopll = Instance("PLLE2_ADV",
            p_CLKIN1_PERIOD        = 16.666,
            p_BANDWIDTH            = "OPTIMIZED",
            p_COMPENSATION         = "ZHOLD",
            p_STARTUP_WAIT         = "FALSE",
            p_DIVCLK_DIVIDE        = 1,
            p_CLKFBOUT_MULT        = self.ADAT_MULT_48k,
            p_CLKFBOUT_PHASE       = self.NO_PHASE_SHIFT,

            # ADAT clock = 12.288 MHz = 48 kHz * 256
            p_CLKOUT2_DIVIDE       = self.ADAT_DIV_48k,
            p_CLKOUT2_PHASE        = self.NO_PHASE_SHIFT,
            p_CLKOUT2_DUTY_CYCLE   = self.DUTY_CYCLE,


            # ADAT sampling clock = 48 kHz * 256 * 8 times oversampling
            p_CLKOUT0_DIVIDE       = self.ADAT_DIV_48k / 8,
            p_CLKOUT0_PHASE        = self.NO_PHASE_SHIFT,
            p_CLKOUT0_DUTY_CYCLE   = self.DUTY_CYCLE,

            # ADAT transmit domain clock = 48 kHz * 256 * 5 output terminals
            p_CLKOUT3_DIVIDE       = self.ADAT_DIV_48k / 5,
            p_CLKOUT3_PHASE        = self.NO_PHASE_SHIFT,
            p_CLKOUT3_DUTY_CYCLE   = self.DUTY_CYCLE,

            i_CLKFBIN              = audiopll_feedback,
            o_CLKFBOUT             = audiopll_feedback,
            i_CLKIN1               = main_clocks[0],
            o_CLKOUT0              = audio_clocks[2],
            o_CLKOUT2              = audio_clocks[0],
            o_CLKOUT3              = audio_clocks[3],
            o_LOCKED               = audio_locked,
        )

        VCO_SCALER_FAST = 1

        m.submodules.fastpll = Instance("PLLE2_ADV",
            p_CLKIN1_PERIOD        = 10.172,
            p_BANDWIDTH            = "OPTIMIZED",
            p_COMPENSATION         = "ZHOLD",
            p_STARTUP_WAIT         = "FALSE",
            p_DIVCLK_DIVIDE        = 1,
            p_CLKFBOUT_MULT        = VCO_SCALER_FAST * platform.fast_multiplier,
            p_CLKFBOUT_PHASE       = self.NO_PHASE_SHIFT,

            # Fast clock = 48 kHz * 256 * 9
            p_CLKOUT0_DIVIDE       = VCO_SCALER_FAST,
            p_CLKOUT0_PHASE        = self.NO_PHASE_SHIFT,
            p_CLKOUT0_DUTY_CYCLE   = self.DUTY_CYCLE,

            # I2S DAC clock 48k = 3.072 MHz = 48 kHz * 32 bit * 2 channels
            p_CLKOUT1_DIVIDE       = VCO_SCALER_FAST * platform.fast_multiplier * 4,
            p_CLKOUT1_PHASE        = self.NO_PHASE_SHIFT,
            p_CLKOUT1_DUTY_CYCLE   = self.DUTY_CYCLE,

            i_CLKFBIN              = fastpll_feedback,
            o_CLKFBOUT             = fastpll_feedback,
            i_CLKIN1               = audio_clocks[2],
            o_CLKOUT0              = fast_clock_48k,
            o_CLKOUT1              = audio_clocks[1],
            o_LOCKED               = fast_locked,
        )


        m.d.comb += [
            reset.eq(~(sys_locked & audio_locked & fast_locked)),
            ClockSignal("fast").eq(fast_clock_48k),
            ClockSignal("usb") .eq(main_clocks[0]),
            ClockSignal("adat").eq(audio_clocks[0]),
            ClockSignal("dac").eq(audio_clocks[1]),
            ClockSignal("sync").eq(audio_clocks[3]),
        ]

        self.wire_up_reset(m, reset)

        return m

class ColorlightDomainGenerator(Elaboratable, ClockDomainGeneratorBase):
    """ Clock generator for the Colorlight I5 board. """
    FastDomainDivider = 7
    FastClockFreq     = 25e6 * 29 / FastDomainDivider

    def __init__(self, clock_frequencies=None):
        pass

    def elaborate(self, platform):
        m = Module()

        # Create our domains.
        m.domains.sync   = ClockDomain("sync")
        m.domains.usb    = ClockDomain("usb")
        m.domains.fast   = ClockDomain("fast")
        m.domains.adat   = ClockDomain("adat")
        m.domains.dac    = ClockDomain("dac")


        # Grab our clock and global reset signals.
        clk25 = flipped(platform.request(platform.default_clk))

        main_clocks    = Signal(5)
        audio_clocks   = Signal(4)
        fast_clock_48k = Signal()

        main_locked   = Signal()
        audio_locked  = Signal()
        fast_locked   = Signal()
        reset         = Signal()

        # USB PLL
        main_feedback    = Signal()
        m.submodules.main_pll = Instance("EHXPLLL",

                # Status.
                o_LOCK=main_locked,

                # PLL parameters...
                p_PLLRST_ENA="DISABLED",
                p_INTFB_WAKE="DISABLED",
                p_STDBY_ENABLE="DISABLED",
                p_DPHASE_SOURCE="DISABLED",
                p_OUTDIVIDER_MUXA="DIVA",
                p_OUTDIVIDER_MUXB="DIVB",
                p_OUTDIVIDER_MUXC="DIVC",
                p_OUTDIVIDER_MUXD="DIVD",

                # 60 MHz
                p_CLKI_DIV = 5,
                p_CLKOP_ENABLE = "ENABLED",
                p_CLKOP_DIV = 10,
                p_CLKOP_CPHASE = 15,
                p_CLKOP_FPHASE = 0,

                p_FEEDBK_PATH = "CLKOP",
                p_CLKFB_DIV = 12,

                # Clock in.
                i_CLKI=clk25,

                # Internal feedback.
                i_CLKFB=main_feedback,

                # Control signals.
                i_RST=reset,
                i_PHASESEL0=0,
                i_PHASESEL1=0,
                i_PHASEDIR=1,
                i_PHASESTEP=1,
                i_PHASELOADREG=1,
                i_STDBY=0,
                i_PLLWAKESYNC=0,

                # Output Enables.
                i_ENCLKOP=0,
                i_ENCLKOS=0,

                # Generated clock outputs.
                o_CLKOP=main_feedback,

                # Synthesis attributes.
                a_FREQUENCY_PIN_CLKI="25",
                a_FREQUENCY_PIN_CLKOP="60",

                a_ICP_CURRENT="6",
                a_LPF_RESISTOR="16",
                a_MFG_ENABLE_FILTEROPAMP="1",
                a_MFG_GMCREF_SEL="2"
        )

        audio_feedback = Signal()
        audio_locked   = Signal()
        m.submodules.audio_pll = Instance("EHXPLLL",

                # Status.
                o_LOCK=audio_locked,

                # PLL parameters...
                p_PLLRST_ENA="DISABLED",
                p_INTFB_WAKE="DISABLED",
                p_STDBY_ENABLE="DISABLED",
                p_DPHASE_SOURCE="DISABLED",
                p_OUTDIVIDER_MUXA="DIVA",
                p_OUTDIVIDER_MUXB="DIVB",
                p_OUTDIVIDER_MUXC="DIVC",
                p_OUTDIVIDER_MUXD="DIVD",

                # 25MHz * 29 = 725 MHz PLL freqency
                p_CLKI_DIV = 1,
                p_CLKOP_ENABLE = "ENABLED",
                p_CLKOP_DIV = 29,
                p_CLKOP_CPHASE = 0,
                p_CLKOP_FPHASE = 0,

                # 12.288 MHz = 725 MHz / 59
                p_CLKOS_ENABLE = "ENABLED",
                p_CLKOS_DIV = 59,
                p_CLKOS_CPHASE = 0,
                p_CLKOS_FPHASE = 0,

                # fast domain clock
                p_CLKOS3_ENABLE = "ENABLED",
                p_CLKOS3_DIV = self.FastDomainDivider,
                p_CLKOS3_CPHASE = 0,
                p_CLKOS3_FPHASE = 0,

                p_FEEDBK_PATH = "CLKOP",
                p_CLKFB_DIV = 1,

                # Clock in.
                i_CLKI=clk25,

                # Internal feedback.
                i_CLKFB=audio_feedback,

                # Control signals.
                i_RST=reset,
                i_PHASESEL0=0,
                i_PHASESEL1=0,
                i_PHASEDIR=1,
                i_PHASESTEP=1,
                i_PHASELOADREG=1,
                i_STDBY=0,
                i_PLLWAKESYNC=0,

                # Output Enables.
                i_ENCLKOP=0,
                i_ENCLKOS=0,
                i_ENCLKOS3=0,

                # Generated clock outputs.
                o_CLKOP=audio_feedback,
                o_CLKOS=ClockSignal("adat"),
                o_CLKOS3=ClockSignal("fast"),

                # Synthesis attributes.
                a_FREQUENCY_PIN_CLKI="25",
                a_FREQUENCY_PIN_CLKOS="12.288",

                a_ICP_CURRENT="6",
                a_LPF_RESISTOR="16",
                a_MFG_ENABLE_FILTEROPAMP="1",
                a_MFG_GMCREF_SEL="2"
        )

        debug0 = platform.request("debug", 0)
        debug1 = platform.request("debug", 1)
        debug2 = platform.request("debug", 2)
        debug3 = platform.request("debug", 3)

        m.submodules.bclk_div = clk_div = DomainRenamer("adat")(SimpleClockDivider(4))

        reset = Signal()
        # Control our resets.
        m.d.comb += [
            ClockSignal("usb")     .eq(main_feedback),
            ClockSignal("sync")    .eq(ClockSignal("usb")),
            ClockSignal("dac")     .eq(clk_div.clock_out),
            clk_div.clock_enable_in.eq(1),

            reset.eq(~(main_locked & audio_locked)),

            debug0.eq(ClockSignal("usb")),
            debug1.eq(ClockSignal("adat")),
            debug2.eq(ClockSignal("fast")),
            debug3.eq(reset),

        ]

        self.wire_up_reset(m, reset)

        return m
