#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020  The SymbiFlow Authors.
#
# Use of this source code is governed by a ISC-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/ISC
#
# SPDX-License-Identifier: ISC
"""
This file contains a set of functions for encoding PLL and MMCM configuration
for Xilinx Series-7 FPGAs into FASM features.
"""

# =============================================================================

PLL_MMCM_LOCK_TABLE = [
    # This table is composed of:
    # LockRefDly_LockFBDly_LockCnt_LockSatHigh_UnlockCnt
    "40'b00110_00110_1111101000_1111101001_0000000001",
    "40'b00110_00110_1111101000_1111101001_0000000001",
    "40'b01000_01000_1111101000_1111101001_0000000001",
    "40'b01011_01011_1111101000_1111101001_0000000001",
    "40'b01110_01110_1111101000_1111101001_0000000001",
    "40'b10001_10001_1111101000_1111101001_0000000001",
    "40'b10011_10011_1111101000_1111101001_0000000001",
    "40'b10110_10110_1111101000_1111101001_0000000001",
    "40'b11001_11001_1111101000_1111101001_0000000001",
    "40'b11100_11100_1111101000_1111101001_0000000001",
    "40'b11111_11111_1110000100_1111101001_0000000001",
    "40'b11111_11111_1100111001_1111101001_0000000001",
    "40'b11111_11111_1011101110_1111101001_0000000001",
    "40'b11111_11111_1010111100_1111101001_0000000001",
    "40'b11111_11111_1010001010_1111101001_0000000001",
    "40'b11111_11111_1001110001_1111101001_0000000001",
    "40'b11111_11111_1000111111_1111101001_0000000001",
    "40'b11111_11111_1000100110_1111101001_0000000001",
    "40'b11111_11111_1000001101_1111101001_0000000001",
    "40'b11111_11111_0111110100_1111101001_0000000001",
    "40'b11111_11111_0111011011_1111101001_0000000001",
    "40'b11111_11111_0111000010_1111101001_0000000001",
    "40'b11111_11111_0110101001_1111101001_0000000001",
    "40'b11111_11111_0110010000_1111101001_0000000001",
    "40'b11111_11111_0110010000_1111101001_0000000001",
    "40'b11111_11111_0101110111_1111101001_0000000001",
    "40'b11111_11111_0101011110_1111101001_0000000001",
    "40'b11111_11111_0101011110_1111101001_0000000001",
    "40'b11111_11111_0101000101_1111101001_0000000001",
    "40'b11111_11111_0101000101_1111101001_0000000001",
    "40'b11111_11111_0100101100_1111101001_0000000001",
    "40'b11111_11111_0100101100_1111101001_0000000001",
    "40'b11111_11111_0100101100_1111101001_0000000001",
    "40'b11111_11111_0100010011_1111101001_0000000001",
    "40'b11111_11111_0100010011_1111101001_0000000001",
    "40'b11111_11111_0100010011_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001",
    "40'b11111_11111_0011111010_1111101001_0000000001"
]

PLL_BANDWIDTH_TABLE = {
    "low": [
        # CP_RES_LFHF
        "10'b0010_1111_00",
        "10'b0010_1111_00",
        "10'b0010_0111_00",
        "10'b0010_1101_00",
        "10'b0010_0101_00",
        "10'b0010_0101_00",
        "10'b0010_1001_00",
        "10'b0010_1110_00",
        "10'b0010_1110_00",
        "10'b0010_0001_00",
        "10'b0010_0001_00",
        "10'b0010_0110_00",
        "10'b0010_0110_00",
        "10'b0010_0110_00",
        "10'b0010_0110_00",
        "10'b0010_1010_00",
        "10'b0010_1010_00",
        "10'b0010_1010_00",
        "10'b0010_1010_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0011_1100_00",
        "10'b0011_1100_00",
        "10'b0011_1100_00",
        "10'b0011_1100_00",
        "10'b0011_1100_00",
        "10'b0011_1100_00",
        "10'b0011_1100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00"
    ],
    "high": [
        # CP_RES_LFHF
        "10'b0011_0111_00",
        "10'b0011_0111_00",
        "10'b0101_1111_00",
        "10'b0111_1111_00",
        "10'b0111_1011_00",
        "10'b1101_0111_00",
        "10'b1110_1011_00",
        "10'b1110_1101_00",
        "10'b1111_1101_00",
        "10'b1111_0111_00",
        "10'b1111_1011_00",
        "10'b1111_1101_00",
        "10'b1111_0011_00",
        "10'b1110_0101_00",
        "10'b1111_0101_00",
        "10'b1111_0101_00",
        "10'b1111_0101_00",
        "10'b1111_0101_00",
        "10'b0111_0110_00",
        "10'b0111_0110_00",
        "10'b0111_0110_00",
        "10'b0111_0110_00",
        "10'b0101_1100_00",
        "10'b0101_1100_00",
        "10'b0101_1100_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b0100_0010_00",
        "10'b0100_0010_00",
        "10'b0100_0010_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0011_0100_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00"
    ],
    "optimized": [
        # CP_RES_LFHF
        "10'b0011_0111_00",
        "10'b0011_0111_00",
        "10'b0101_1111_00",
        "10'b0111_1111_00",
        "10'b0111_1011_00",
        "10'b1101_0111_00",
        "10'b1110_1011_00",
        "10'b1110_1101_00",
        "10'b1111_1101_00",
        "10'b1111_0111_00",
        "10'b1111_1011_00",
        "10'b1111_1101_00",
        "10'b1111_0011_00",
        "10'b1110_0101_00",
        "10'b1111_0101_00",
        "10'b1111_0101_00",
        "10'b1111_0101_00",
        "10'b1111_0101_00",
        "10'b0111_0110_00",
        "10'b0111_0110_00",
        "10'b0111_0110_00",
        "10'b0111_0110_00",
        "10'b0101_1100_00",
        "10'b0101_1100_00",
        "10'b0101_1100_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b0100_0010_00",
        "10'b0100_0010_00",
        "10'b0100_0010_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0011_0100_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00"
    ],
}

# =============================================================================

MMCM_BANDWIDTH_TABLE = {
    "low": [
        # CP_RES_LFHF
        "10'b0010_1111_00",
        "10'b0010_1111_00",
        "10'b0010_1111_00",
        "10'b0010_1111_00",
        "10'b0010_0111_00",
        "10'b0010_1011_00",
        "10'b0010_1101_00",
        "10'b0010_0011_00",
        "10'b0010_0101_00",
        "10'b0010_0101_00",
        "10'b0010_1001_00",
        "10'b0010_1110_00",
        "10'b0010_1110_00",
        "10'b0010_1110_00",
        "10'b0010_1110_00",
        "10'b0010_0001_00",
        "10'b0010_0001_00",
        "10'b0010_0001_00",
        "10'b0010_0110_00",
        "10'b0010_0110_00",
        "10'b0010_0110_00",
        "10'b0010_0110_00",
        "10'b0010_0110_00",
        "10'b0010_0110_00",
        "10'b0010_0110_00",
        "10'b0010_1010_00",
        "10'b0010_1010_00",
        "10'b0010_1010_00",
        "10'b0010_1010_00",
        "10'b0010_1010_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_1100_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00",
        "10'b0010_0010_00"
    ],
    "high": [
        # CP_RES_LFHF
        "10'b0010_1111_00",
        "10'b0100_1111_00",
        "10'b0101_1011_00",
        "10'b0111_0111_00",
        "10'b1101_0111_00",
        "10'b1110_1011_00",
        "10'b1110_1101_00",
        "10'b1111_0011_00",
        "10'b1110_0101_00",
        "10'b1111_0101_00",
        "10'b1111_1001_00",
        "10'b1101_0001_00",
        "10'b1111_1001_00",
        "10'b1111_1001_00",
        "10'b1111_1001_00",
        "10'b1111_1001_00",
        "10'b1111_0101_00",
        "10'b1111_0101_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b0101_1100_00",
        "10'b0101_1100_00",
        "10'b0101_1100_00",
        "10'b0101_1100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0111_0001_00",
        "10'b0111_0001_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0110_0001_00",
        "10'b0110_0001_00",
        "10'b0101_0110_00",
        "10'b0101_0110_00",
        "10'b0101_0110_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0100_1010_00",
        "10'b0011_1100_00",
        "10'b0011_1100_00"
    ],
    "optimized": [
        # CP_RES_LFHF
        "10'b0010_1111_00",
        "10'b0100_1111_00",
        "10'b0101_1011_00",
        "10'b0111_0111_00",
        "10'b1101_0111_00",
        "10'b1110_1011_00",
        "10'b1110_1101_00",
        "10'b1111_0011_00",
        "10'b1110_0101_00",
        "10'b1111_0101_00",
        "10'b1111_1001_00",
        "10'b1101_0001_00",
        "10'b1111_1001_00",
        "10'b1111_1001_00",
        "10'b1111_1001_00",
        "10'b1111_1001_00",
        "10'b1111_0101_00",
        "10'b1111_0101_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b1100_0001_00",
        "10'b0101_1100_00",
        "10'b0101_1100_00",
        "10'b0101_1100_00",
        "10'b0101_1100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0011_0100_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0010_1000_00",
        "10'b0111_0001_00",
        "10'b0111_0001_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0100_1100_00",
        "10'b0110_0001_00",
        "10'b0110_0001_00",
        "10'b0101_0110_00",
        "10'b0101_0110_00",
        "10'b0101_0110_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0010_0100_00",
        "10'b0100_1010_00",
        "10'b0011_1100_00",
        "10'b0011_1100_00"
    ],
    "ss": [
        # CP_RES_LFHF
        "10'b0010_1111_11",
        "10'b0010_1111_11",
        "10'b0010_1111_11",
        "10'b0010_1111_11",
        "10'b0010_0111_11",
        "10'b0010_1011_11",
        "10'b0010_1101_11",
        "10'b0010_0011_11",
        "10'b0010_0101_11",
        "10'b0010_0101_11",
        "10'b0010_1001_11",
        "10'b0010_1110_11",
        "10'b0010_1110_11",
        "10'b0010_1110_11",
        "10'b0010_1110_11",
        "10'b0010_0001_11",
        "10'b0010_0001_11",
        "10'b0010_0001_11",
        "10'b0010_0110_11",
        "10'b0010_0110_11",
        "10'b0010_0110_11",
        "10'b0010_0110_11",
        "10'b0010_0110_11",
        "10'b0010_0110_11",
        "10'b0010_0110_11",
        "10'b0010_1010_11",
        "10'b0010_1010_11",
        "10'b0010_1010_11",
        "10'b0010_1010_11",
        "10'b0010_1010_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_1100_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11",
        "10'b0010_0010_11"
    ],
}

# =============================================================================


def compute_pll_lookup(clkfbout_mult, bandwidth):
    """
    Returns values from PLL lookup tables according to its CLKFBOUT_MULT and
    BANDWIDTH parameter. The values are returned as binary strings.
    """

    # Lock table (a.k.a. LKTABLE)
    lktable = PLL_MMCM_LOCK_TABLE[clkfbout_mult - 1].replace("_", "")
    # Bandwidth control table (a.k.a. TABLE)
    table = PLL_BANDWIDTH_TABLE[bandwidth][clkfbout_mult - 1].replace("_", "")

    return lktable, table


def compute_pll_clkregs(divide, duty, phase):
    """
    Returns a string of 0s and 1s with the following layout. Bits are indexed
    starting from LSB.

      CLKREG2: RESERVED[6:0], MX[1:0], EDGE, NO_COUNT, DELAY_TIME[5:0]
      CLKREG1: PHASE_MUX[3:0], RESERVED, HIGH_TIME[5:0], LOW_TIME[5:0]        
    """

    PLL_FRAC_PRECISION = 10

    def round_frac(x, n):
        scale = (1 << PLL_FRAC_PRECISION)
        return int(round(x / scale, n) * scale)

    # Sanity check argument types and values
    assert isinstance(divide, int), type(divide)
    assert divide >= 1 and divide <= 128, divide
    assert isinstance(duty, int) or isinstance(duty, float), type(duty)
    assert duty > 0.0 and duty < 1.0, duty
    assert isinstance(phase, int) or isinstance(phase, float), type(phase)
    assert phase > -360.0 and phase < 360.0, phase

    # Phase shift register fields
    if phase < 0.0:
        phase += 360.0

    phase_fixed = int(phase * (1 << PLL_FRAC_PRECISION))
    phase_in_cycles = round_frac((phase_fixed * divide) // 360, 3)

    phase_mux = (phase_in_cycles >> (PLL_FRAC_PRECISION - 3)) & 0x07
    delay_tim = (phase_in_cycles >> (PLL_FRAC_PRECISION)) & 0x3F

    # Divider register fields
    if divide > 1:

        if divide >= 64:
            duty_min = (divide - 64.0) / divide
            duty_max = 64.0 / divide
            duty = max(duty_min, min(duty, duty_max))

        duty_fix = int(duty * (1 << PLL_FRAC_PRECISION))
        temp = round_frac(duty_fix * divide, 1)

        high_time = (temp >> PLL_FRAC_PRECISION) & 0x7F
        w_edge = (temp >> (PLL_FRAC_PRECISION - 1)) & 0x01

        if high_time == 0:
            high_time = 1
            w_edge = 0

        elif high_time == divide:
            high_time = divide - 1
            w_edge = 1

        low_time = divide - high_time
        no_count = 0

    # No division
    else:
        w_edge = 0
        no_count = 1
        high_time = 1
        low_time = 1

    high_time &= 0x3F
    low_time &= 0x3F

    # Assemble the final clock registers content
    clkregs = low_time
    clkregs |= high_time << 6
    clkregs |= phase_mux << 13

    clkregs |= delay_tim << 16
    clkregs |= no_count << 22
    clkregs |= w_edge << 23

    clkregs = "{:032b}".format(clkregs)[::-1]
    return clkregs


# =============================================================================


def compute_mmcm_lookup(clkfbout_mult, bandwidth, spread_spectrum=False):
    """
    Returns values from MMCM lookup tables according to its CLKFBOUT_MULT and
    BANDWIDTH parameter. MMCM uses one more different lookup when
    spread-spectrum is enabled. The values are returned as binary strings.
    """

    # Lock table (a.k.a. LKTABLE)
    lktable = PLL_MMCM_LOCK_TABLE[clkfbout_mult - 1].replace("_", "")

    # Bandwidth control table (a.k.a. TABLE)
    if spread_spectrum:
        key = "ss"
    else:
        key = bandwidth
    table = MMCM_BANDWIDTH_TABLE[key][clkfbout_mult - 1].replace("_", "")

    return lktable, table


def compute_mmcm_clkregs(divide, duty, phase):
    """
    See compule_pll_clkregs. They are identical.
    """
    return compute_pll_clkregs(divide, duty, phase)


def compute_mmcm_clkregs_frac(divide, duty, phase):
    """
    Returns a string of 0s and 1s with the following layout. Bits are indexed
    starting from LSB.
    
        CLKREG1: PHASE_MUX[2:0], RESERVED, HIGH_TIME[5:0], LOW_TIME[5:0]
        CLKREG2: RESERVED[0:0], FRAC[2:0], FRAC_EN[0:0], FRAC_WF_R[0:0], MX[1:0], EDGE, NO_COUNT, DELAY_TIME[5:0]
        Shared : RESERVED[1:0], FRAC_TIME[2:0], FRAC_WF_FALL
   """

    # Sanity check argument types and values
    assert isinstance(divide, float), type(divide)
    assert divide >= 1.0 and divide <= 128.0, divide