Skip to content

Commit

Permalink
Merge pull request #149 from liambeguin/adrv9009
Browse files Browse the repository at this point in the history
Adrv9009
  • Loading branch information
tfcollins authored Sep 27, 2024
2 parents 80f452c + 001a8ca commit 4eafcef
Show file tree
Hide file tree
Showing 16 changed files with 546 additions and 210 deletions.
95 changes: 86 additions & 9 deletions adijif/clocks/ad9528.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ class ad9528(ad9528_bf):
m1_available = [3, 4, 5]
""" Output dividers """
d_available = [*range(1, 1024)]
""" sysref dividers """
k_available = [*range(0, 65536)]
""" VCXO multiplier """
n2_available = [*range(12, 256)]
n2_available = [*range(1, 256)]
""" VCO calibration dividers """
a_available = [0, 1, 2, 3]
b_availble = [*range(3, 64)]
Expand All @@ -32,7 +34,8 @@ class ad9528(ad9528_bf):
# Defaults
_m1: Union[List[int], int] = [3, 4, 5]
_d: Union[List[int], int] = [*range(1, 1024)]
_n2: Union[List[int], int] = [*range(12, 255)]
_k: Union[List[int], int] = k_available
_n2: Union[List[int], int] = n2_available
_r1: Union[List[int], int] = [*range(1, 32)]
_a: Union[List[int], int] = [*range(0, 4)]
_b: Union[List[int], int] = [*range(3, 64)]
Expand All @@ -47,6 +50,10 @@ class ad9528(ad9528_bf):
use_vcxo_double = False
vcxo = 125e6

# sysref parameters
sysref_external = False
_sysref = None

@property
def m1(self) -> Union[int, List[int]]:
"""VCO divider path 1.
Expand Down Expand Up @@ -95,29 +102,53 @@ def d(self, value: Union[int, List[int]]) -> None:
self._check_in_range(value, self.d_available, "d")
self._d = value

@property
def k(self) -> Union[int, List[int]]:
"""Sysref dividers.
Valid dividers are 0->65535
Returns:
int: Current allowable dividers
"""
return self._k

@k.setter
def k(self, value: Union[int, List[int]]) -> None:
"""Sysref dividers.
Valid dividers are 0->65535
Args:
value (int, list[int]): Allowable values for divider
"""
self._check_in_range(value, self.d_available, "k")
self._k = value

@property
def n2(self) -> Union[int, List[int]]:
"""n2: VCO feedback divider.
Valid dividers are 12->255
Valid dividers are 1->255
Returns:
int: Current allowable dividers
"""
return self._m2
return self._n2

@n2.setter
def n2(self, value: Union[int, List[int]]) -> None:
"""VCO feedback divider.
Valid dividers are 12->255
Valid dividers are 1->255
Args:
value (int, list[int]): Allowable values for divider
"""
self._check_in_range(value, self.n2_available, "n2")
self._m2 = value
self._n2 = value

@property
def r1(self) -> Union[int, List[int]]:
Expand Down Expand Up @@ -191,6 +222,35 @@ def b(self, value: Union[int, List[int]]) -> None:
self._check_in_range(value, self.b_available, "b")
self._b = value

@property
def vco(self):
r1 = self._get_val(self.config["r1"])
m1 = self._get_val(self.config["m1"])
n2 = self._get_val(self.config["n2"])

return self.vcxo / r1 * m1 * n2

@property
def sysref(self):
"""SYSREF Frequency
Returns:
float: computed sysref frequency
"""
r1 = self._get_val(self.config["r1"])
k = self._get_val(self.config["k"])

if self.sysref_external:
sysref_src = self.vcxo
else:
sysref_src = self.vcxo / r1

return sysref_src / (2 * k)

@sysref.setter
def sysref(self, value: Union[int, float]):
self._sysref = int(value)

def get_config(self, solution: CpoSolveResult = None) -> Dict:
"""Extract configurations from solver results.
Expand All @@ -216,6 +276,8 @@ def get_config(self, solution: CpoSolveResult = None) -> Dict:
out_dividers = [self._get_val(x) for x in self.config["out_dividers"]]

config: Dict = {
"vcxo": self.vcxo / 2 if self.use_vcxo_double else self.vcxo,
"vco": self.vco,
"r1": self._get_val(self.config["r1"]),
"n2": self._get_val(self.config["n2"]),
"m1": self._get_val(self.config["m1"]),
Expand All @@ -225,6 +287,10 @@ def get_config(self, solution: CpoSolveResult = None) -> Dict:
"output_clocks": [],
}

if self._sysref:
config["k"] = self._get_val(self.config["k"])
config["sysref"] = self.sysref

clk = self.vcxo * config["n2"] / config["r1"]

output_cfg = {}
Expand Down Expand Up @@ -256,6 +322,7 @@ def _setup_solver_constraints(self, vcxo: int) -> None:
self.config = {
"r1": self._convert_input(self._r1, "r1"),
"m1": self._convert_input(self._m1, "m1"),
"k": self._convert_input(self._k, "k"),
"n2": self._convert_input(self._n2, "n2"),
"a": self._convert_input(self._a, "a"),
"b": self._convert_input(self._b, "b"),
Expand Down Expand Up @@ -318,7 +385,7 @@ def _get_clock_constraint(
return self.vcxo / self.config["r1"] * self.config["n2"] / od

def set_requested_clocks(
self, vcxo: int, out_freqs: List, clk_names: List[str]
self, vcxo: int, out_freqs: List, clk_names: List[str],
) -> None:
"""Define necessary clocks to be generated in model.
Expand All @@ -337,10 +404,20 @@ def set_requested_clocks(
# Setup clock chip internal constraints
self._setup(vcxo)

if self._sysref:
if self.sysref_external:
sysref_src = self.vcxo
else:
sysref_src = self.vcxo / self.config["r1"]

self._add_equation(
[sysref_src / (2 * self.config["k"]) == self._sysref]
)

# Add requested clocks to output constraints
for out_freq in out_freqs:
for out_freq, name in zip(out_freqs, clk_names):
# od = self.model.Var(integer=True, lb=1, ub=256, value=1)
od = self._convert_input(self._d, "d_" + str(out_freq))
od = self._convert_input(self._d, f"d_{name}_{out_freq}")
# od = self.model.sos1([n*n for n in range(1,9)])
self._add_equation(
[self.vcxo / self.config["r1"] * self.config["n2"] / od == out_freq]
Expand Down
71 changes: 18 additions & 53 deletions adijif/converters/ad9081.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ class ad9081_core(converter, metaclass=ABCMeta):
config = {} # type: ignore

device_clock_max = 12e9
_model_type = "adc"
_lmfc_divisor_sysref_available = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]

def _check_valid_internal_configuration(self) -> None:
Expand Down Expand Up @@ -264,15 +263,9 @@ def get_required_clocks(self) -> List:
self._lmfc_divisor_sysref_available, "lmfc_divisor_sysref"
)

if self.solver == "gekko":
self.config["sysref"] = self.model.Intermediate(
self.multiframe_clock # type: ignore
/ self.config["lmfc_divisor_sysref"]
)
elif self.solver == "CPLEX":
self.config["sysref"] = (
self.multiframe_clock / self.config["lmfc_divisor_sysref"]
)
self.config["sysref"] = self._add_intermediate(
self.multiframe_clock / self.config["lmfc_divisor_sysref"]
)

# Device Clocking
if self.clocking_option == "direct":
Expand All @@ -291,7 +284,6 @@ def get_required_clocks(self) -> List:
class ad9081_rx(adc, ad9081_core):
"""AD9081 Receive model."""

_model_type = "adc"
name = "AD9081_RX"

converter_clock_min = 1.45e9
Expand Down Expand Up @@ -369,15 +361,9 @@ def _converter_clock_config(self) -> None:
adc_clk = self.decimation * self.sample_clock
self.config["l"] = self._convert_input([1, 2, 3, 4], "l")
self.config["adc_clk"] = self._convert_input(adc_clk)

if self.solver == "gekko":
self.config["converter_clk"] = self.model.Intermediate(
self.config["adc_clk"] * self.config["l"]
)
elif self.solver == "CPLEX":
self.config["converter_clk"] = self.config["adc_clk"] * self.config["l"]
else:
raise Exception(f"Unknown solver {self.solver}")
self.config["converter_clk"] = self._add_intermediate(
self.config["adc_clk"] * self.config["l"]
)

def _check_valid_internal_configuration(self) -> None:
mode = self._check_valid_jesd_mode()
Expand Down Expand Up @@ -410,7 +396,6 @@ def _check_valid_internal_configuration(self) -> None:
class ad9081_tx(dac, ad9081_core):
"""AD9081 Transmit model."""

_model_type = "dac"
name = "AD9081_TX"

converter_clock_min = 2.9e9
Expand Down Expand Up @@ -492,14 +477,9 @@ def _converter_clock_config(self) -> None:
"""
dac_clk = self.interpolation * self.sample_clock
self.config["dac_clk"] = self._convert_input(dac_clk)
if self.solver == "gekko":
self.config["converter_clk"] = self.model.Intermediate(
self.config["dac_clk"]
)
elif self.solver == "CPLEX":
self.config["converter_clk"] = self.config["dac_clk"]
else:
raise Exception(f"Unknown solver {self.solver}")
self.config["converter_clk"] = self._add_intermediate(
self.config["dac_clk"]
)


class ad9081(ad9081_core):
Expand Down Expand Up @@ -571,14 +551,9 @@ def _converter_clock_config(self) -> None:

self.config["dac_clk"] = self._convert_input(dac_clk)
self.config["adc_clk"] = self._convert_input(adc_clk)
if self.solver == "gekko":
self.config["converter_clk"] = self.model.Intermediate(
self.config["dac_clk"]
)
elif self.solver == "CPLEX":
self.config["converter_clk"] = self.config["dac_clk"]
else:
raise Exception(f"Unknown solver {self.solver}")
self.config["converter_clk"] = self._add_intermediate(
self.config["dac_clk"]
)

# Add single PLL constraint
# JESD204B/C transmitter is a power of 2 divisor of the lane rate of
Expand Down Expand Up @@ -617,22 +592,12 @@ def get_required_clocks(self) -> List:
self.dac._dac_lmfc_divisor_sysref, "dac_lmfc_divisor_sysref"
)

if self.solver == "gekko":
self.config["sysref_adc"] = self.model.Intermediate(
self.adc.multiframe_clock / self.config["adc_lmfc_divisor_sysref"]
)
self.config["sysref_dac"] = self.model.Intermediate(
self.dac.multiframe_clock / self.config["dac_lmfc_divisor_sysref"]
)
elif self.solver == "CPLEX":
self.config["sysref_adc"] = self.adc.multiframe_clock / (
self.config["adc_lmfc_divisor_sysref"]
)
self.config["sysref_dac"] = self.dac.multiframe_clock / (
self.config["dac_lmfc_divisor_sysref"]
)
else:
raise Exception(f"Unknown solver {self.solver}")
self.config["sysref_adc"] = self._add_intermediate(
self.adc.multiframe_clock / self.config["adc_lmfc_divisor_sysref"]
)
self.config["sysref_dac"] = self._add_intermediate(
self.dac.multiframe_clock / self.config["dac_lmfc_divisor_sysref"]
)

# Device Clocking
if self.clocking_option == "direct":
Expand Down
Loading

0 comments on commit 4eafcef

Please sign in to comment.