Skip to content

Commit

Permalink
Merge pull request #158 from analogdevicesinc/tfcollins/fpga-pll-fixes
Browse files Browse the repository at this point in the history
Add separate models for Xilinx transceivers for each generation
  • Loading branch information
tfcollins authored Dec 12, 2024
2 parents 77bf513 + d85a8b8 commit 996d384
Show file tree
Hide file tree
Showing 55 changed files with 3,538 additions and 2,123 deletions.
5 changes: 4 additions & 1 deletion adijif/cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Console script for adijif."""

import sys
from typing import List

Expand All @@ -12,7 +13,9 @@ def main(args: List[str] = None) -> None:
Args:
args (List[str]): List of input argument
"""
click.echo("Replace this message by putting your code into " "adijif.cli.main")
click.echo(
"Replace this message by putting your code into " "adijif.cli.main"
)
click.echo("See click documentation at https://click.palletsprojects.com/")


Expand Down
10 changes: 9 additions & 1 deletion adijif/clocks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
"""ADI JIF clock chip models."""
supported_parts = ["ad9523_1", "ad9528", "ad9545", "hmc7044", "ltc6952", "ltc6953"]

supported_parts = [
"ad9523_1",
"ad9528",
"ad9545",
"hmc7044",
"ltc6952",
"ltc6953",
]
36 changes: 30 additions & 6 deletions adijif/clocks/ad9523.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""AD9523-1 clock chip model."""

from typing import Dict, List, Union

from adijif.clocks.ad9523_1_bf import ad9523_1_bf
Expand All @@ -24,7 +25,18 @@ class ad9523_1(ad9523_1_bf):
# Defaults
_m1: Union[List[int], int] = [3, 4, 5]
_d: Union[List[int], int] = [*range(1, 1024)]
_n2: Union[List[int], int] = [12, 16, 17, 20, 21, 22, 24, 25, 26, *range(28, 255)]
_n2: Union[List[int], int] = [
12,
16,
17,
20,
21,
22,
24,
25,
26,
*range(28, 255),
]
_r2: Union[List[int], int] = list(range(1, 31 + 1))

# Limits
Expand Down Expand Up @@ -154,7 +166,9 @@ def get_config(self, solution: CpoSolveResult = None) -> Dict:
Exception: If solver is not called first
"""
if not self._clk_names:
raise Exception("set_requested_clocks must be called before get_config")
raise Exception(
"set_requested_clocks must be called before get_config"
)
for k in ["out_dividers", "m1", "n2", "r2"]:
if k not in self.config.keys():
raise Exception("Missing key: " + str(k))
Expand All @@ -165,11 +179,15 @@ def get_config(self, solution: CpoSolveResult = None) -> Dict:
"m1": self._get_val(self.config["m1"]),
"n2": self._get_val(self.config["n2"]),
"r2": self._get_val(self.config["r2"]),
"out_dividers": [self._get_val(x) for x in self.config["out_dividers"]],
"out_dividers": [
self._get_val(x) for x in self.config["out_dividers"]
],
"output_clocks": [],
}

config["vcxo"] = self._get_val(self.vcxo) # pytype: disable=attribute-error
config["vcxo"] = self._get_val(
self.vcxo
) # pytype: disable=attribute-error
vcxo = config["vcxo"]

clk = vcxo / config["r2"] * config["n2"] / config["m1"]
Expand All @@ -184,7 +202,9 @@ def get_config(self, solution: CpoSolveResult = None) -> Dict:

return config

def _setup_solver_constraints(self, vcxo: Union[float, int, CpoIntVar]) -> None:
def _setup_solver_constraints(
self, vcxo: Union[float, int, CpoIntVar]
) -> None:
"""Apply constraints to solver model.
Args:
Expand Down Expand Up @@ -261,7 +281,11 @@ def _get_clock_constraint(
od = self._convert_input(self._d, "d_" + str(clk_name))
self.config["out_dividers"].append(od)
return (
self.vcxo / self.config["r2"] * self.config["n2"] / self.config["m1"] / od
self.vcxo
/ self.config["r2"]
* self.config["n2"]
/ self.config["m1"]
/ od
)

def set_requested_clocks(
Expand Down
3 changes: 2 additions & 1 deletion adijif/clocks/ad9523_1_bf.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ def list_available_references(self, divider_set):
"Input must be of type dict with fields: " + str(ref.keys())
)
return [
divider_set["vco"] / divider_set["m1"] / div for div in self.d_available
divider_set["vco"] / divider_set["m1"] / div
for div in self.d_available
]

def find_dividers(self, vcxo, required_output_rates, find=3):
Expand Down
24 changes: 19 additions & 5 deletions adijif/clocks/ad9528.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""AD9528 clock chip model."""

from typing import Dict, List, Union

from adijif.clocks.ad9528_bf import ad9528_bf
Expand Down Expand Up @@ -277,7 +278,9 @@ def get_config(self, solution: CpoSolveResult = None) -> Dict:
Exception: If solver is not called first
"""
if not self._clk_names:
raise Exception("set_requested_clocks must be called before get_config")
raise Exception(
"set_requested_clocks must be called before get_config"
)

if solution:
self.solution = solution
Expand Down Expand Up @@ -345,9 +348,15 @@ def _setup_solver_constraints(self, vcxo: int) -> None:
self._add_equation(
[
self.vcxo / self.config["r1"] <= self.pfd_max,
self.vcxo / self.config["r1"] * self.config["m1"] * self.config["n2"]
self.vcxo
/ self.config["r1"]
* self.config["m1"]
* self.config["n2"]
<= self.vco_max,
self.vcxo / self.config["r1"] * self.config["m1"] * self.config["n2"]
self.vcxo
/ self.config["r1"]
* self.config["m1"]
* self.config["n2"]
>= self.vco_min,
4 * self.config["b"] + self.config["a"] >= 16,
4 * self.config["b"] + self.config["a"]
Expand Down Expand Up @@ -423,14 +432,19 @@ def set_requested_clocks(
else:
sysref_src = self.vcxo / self.config["r1"]

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

# Add requested clocks to output constraints
for out_freq, name in zip(out_freqs, clk_names): # noqa: B905
# od = self.model.Var(integer=True, lb=1, ub=256, value=1)
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]
[
self.vcxo / self.config["r1"] * self.config["n2"] / od
== out_freq
]
)
self.config["out_dividers"].append(od)
11 changes: 8 additions & 3 deletions adijif/clocks/ad9528_bf.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ def list_available_references(self, divider_set):
"Input must be of type dict with fields: " + str(ref.keys())
)
return [
divider_set["vco"] / divider_set["m1"] / div for div in self.d_available
divider_set["vco"] / divider_set["m1"] / div
for div in self.d_available
]

def find_dividers(self, vcxo, required_output_rates, find=3):
Expand All @@ -55,8 +56,12 @@ def find_dividers(self, vcxo, required_output_rates, find=3):
and vco < self.vco_max
and (vco / m1) % mod == 0
):
required_output_divs = (vco / m1) / required_output_rates
if np.all(np.in1d(required_output_divs, self.d_available)):
required_output_divs = (
vco / m1
) / required_output_rates
if np.all(
np.in1d(required_output_divs, self.d_available)
):
configs.append(
{
"m1": m1,
Expand Down
70 changes: 52 additions & 18 deletions adijif/clocks/ad9545.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,9 @@ def get_config(self, solution: CpoSolveResult = None) -> Dict:
self.config[n_dpll_name]
)

config["PLL" + str(i)][n_name] = self._get_val(self.config[n_name])
config["PLL" + str(i)][n_name] = self._get_val(
self.config[n_name]
)

config["PLL" + str(i)]["rate_hz"] = self._get_val(
self.config["PLL" + str(i) + "_rate"]
Expand All @@ -183,7 +185,9 @@ def get_config(self, solution: CpoSolveResult = None) -> Dict:

if self.PLL_used[i] and dpll_profile_name in self.profiles:
if self.profiles[dpll_profile_name]["hitless"]:
source_nr = int(self.profiles[dpll_profile_name]["fb_source"])
source_nr = int(
self.profiles[dpll_profile_name]["fb_source"]
)

config["PLL" + str(i)]["hitless"] = {
"fb_source": source_nr,
Expand Down Expand Up @@ -259,11 +263,18 @@ def _setup_solver_constraints(
for j in range(0, 4):
if input_refs[j] != 0:
n_name = "n" + str(i) + "_profile_" + str(j)
n_dpll_name = "n_dpll" + str(i) + "_profile_" + str(j)
m_apll_name = "m_apll" + str(i) + "_profile_" + str(j)
n_dpll_name = (
"n_dpll" + str(i) + "_profile_" + str(j)
)
m_apll_name = (
"m_apll" + str(i) + "_profile_" + str(j)
)

self.config[n_name] = self.model.Var(
integer=True, lb=self.N_min, ub=self.N_max, name=n_name
integer=True,
lb=self.N_min,
ub=self.N_max,
name=n_name,
)

""" Internally the PLL block is composed of a
Expand Down Expand Up @@ -317,8 +328,12 @@ def _setup_solver_constraints(
for j in range(0, 4):
if input_refs[j] != 0:
n_name = "n" + str(i) + "_profile_" + str(j)
n_dpll_name = "n_dpll" + str(i) + "_profile_" + str(j)
m_apll_name = "m_apll" + str(i) + "_profile_" + str(j)
n_dpll_name = (
"n_dpll" + str(i) + "_profile_" + str(j)
)
m_apll_name = (
"m_apll" + str(i) + "_profile_" + str(j)
)

self.config[n_name] = exp.integer_var(
int(self.N_min), int(self.N_max), n_name
Expand All @@ -328,9 +343,13 @@ def _setup_solver_constraints(
PLL and an Analog PLL with different constraints on
dividers
"""
DPLL_N = exp.integer_var(int(1), int(350e6), n_dpll_name)
DPLL_N = exp.integer_var(
int(1), int(350e6), n_dpll_name
)
self.config[n_dpll_name] = DPLL_N
APLL_M = exp.integer_var(int(7), int(255), m_apll_name)
APLL_M = exp.integer_var(
int(7), int(255), m_apll_name
)
self.config[m_apll_name] = APLL_M

equations = equations + [
Expand Down Expand Up @@ -358,7 +377,8 @@ def _setup_solver_constraints(
raise Exception("Unknown solver {}".format(self.solver))

equations = equations + [
self.config["PLL_in_rate_" + str(i)] * self.config["r" + str(i)]
self.config["PLL_in_rate_" + str(i)]
* self.config["r" + str(i)]
== self.config["input_ref_" + str(i)]
]

Expand Down Expand Up @@ -400,18 +420,22 @@ def _setup_solver_constraints(
if self.avoid_min_max_PLL_rates:
for i in range(0, 2):
if self.PLL_used[i]:
average_PLL_rate = self.PLL_out_min[i] / 2 + self.PLL_out_max[i] / 2
average_PLL_rate = (
self.PLL_out_min[i] / 2 + self.PLL_out_max[i] / 2
)

if self.solver == "CPLEX":
cplex_objectives = cplex_objectives + [
mod.abs(
self.config["PLL" + str(i) + "_rate"] - average_PLL_rate
self.config["PLL" + str(i) + "_rate"]
- average_PLL_rate
)
]
elif self.solver == "gekko":
self.model.Minimize(
self.model.abs3(
self.config["PLL" + str(i) + "_rate"] - average_PLL_rate
self.config["PLL" + str(i) + "_rate"]
- average_PLL_rate
)
)
else:
Expand All @@ -426,7 +450,9 @@ def _setup_solver_constraints(
self.config["r" + str(i)]
]
elif self.solver == "gekko":
self.model.Maximize(self.config["PLL_in_rate_" + str(i)])
self.model.Maximize(
self.config["PLL_in_rate_" + str(i)]
)
else:
raise Exception("Unknown solver {}".format(self.solver))

Expand Down Expand Up @@ -489,8 +515,13 @@ def set_requested_clocks(self, ins: List[int], outs: List[int]) -> None:
for j in range(0, 4):
dpll_profile_name = "dpll_" + str(i) + "_profile_" + str(j)

if self.PLL_used[i] and self.profiles[dpll_profile_name]["hitless"]:
source_nr = int(self.profiles[dpll_profile_name]["fb_source"])
if (
self.PLL_used[i]
and self.profiles[dpll_profile_name]["hitless"]
):
source_nr = int(
self.profiles[dpll_profile_name]["fb_source"]
)
n_dpll_name = "n_dpll" + str(i) + "_profile_" + str(j)

if out_freqs[source_nr] == 0:
Expand All @@ -508,7 +539,9 @@ def set_requested_clocks(self, ins: List[int], outs: List[int]) -> None:
""" Frequency translation factor:
N * input_ref_j == out_rate_x * r_div_j
"""
self._add_equation([input_ref * pll_n_div == out_rate * r_div])
self._add_equation(
[input_ref * pll_n_div == out_rate * r_div]
)

""" Hitless mode places a strict constraint on Q dividers """
self.config["q" + str(i)]
Expand All @@ -524,7 +557,8 @@ def set_requested_clocks(self, ins: List[int], outs: List[int]) -> None:
self._add_equation(
[
self.config["PLL" + str(pll_number) + "_rate"]
== self.config["out_rate_" + str(i)] * self.config["q" + str(i)]
== self.config["out_rate_" + str(i)]
* self.config["q" + str(i)]
]
)

Expand Down
1 change: 1 addition & 0 deletions adijif/clocks/clock.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Clock parent metaclass to maintain consistency for all clock chip."""

from abc import ABCMeta, abstractmethod
from typing import Dict, List, Union

Expand Down
Loading

0 comments on commit 996d384

Please sign in to comment.