Skip to content

Commit

Permalink
Add drawing features to AD9680 plus minor fixes. Plus generic clock s…
Browse files Browse the repository at this point in the history
…ource

Signed-off-by: Travis F. Collins <[email protected]>
  • Loading branch information
tfcollins committed Jun 6, 2024
1 parent 96b7444 commit efe318f
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 5 deletions.
2 changes: 1 addition & 1 deletion adijif/converters/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
"""ADI JIF converter models."""
supported_parts = ["ad9680", "adrv9009", "ad9081_rx", "ad9144"]
supported_parts = ["ad9680", "adrv9009", "ad9081_rx", "ad9081_tx", "ad9144"]
8 changes: 6 additions & 2 deletions adijif/converters/ad9680.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from adijif.converters.ad9680_bf import ad9680_bf

from ..solvers import CpoSolveResult # noqa: I202
from .ad9680_draw import ad9680_draw # noqa: I202


def _convert_to_config(
Expand All @@ -17,7 +18,7 @@ def _convert_to_config(
CS: Union[int, float],
) -> Dict:
# return {"L": L, "M": M, "F": F, "S": S, "HD": HD, "N": N, "Np": Np, "CS": CS}
return {"L": L, "M": M, "F": F, "S": S, "HD": HD, "Np": Np}
return {"L": L, "M": M, "F": F, "S": S, "HD": HD, "Np": Np, "jesd_class": "jesd204b"}


quick_configuration_modes = {
Expand All @@ -43,7 +44,7 @@ def _convert_to_config(
}


class ad9680(ad9680_bf):
class ad9680(ad9680_bf, ad9680_draw):
"""AD9680 high speed ADC model.
This model supports direct clock configurations
Expand Down Expand Up @@ -110,6 +111,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: # noqa: ANN401
self.K = 32
self.sample_clock = 1e9
super().__init__(*args, **kwargs)
self._init_diagram()

def _check_valid_jesd_mode(self) -> str:
"""Verify current JESD configuration for part is valid.
Expand Down Expand Up @@ -140,6 +142,8 @@ def get_config(self, solution: CpoSolveResult = None) -> Dict:
Returns:
Dict: Dictionary of clocking rates and dividers for configuration
"""
if solution:
self._saved_solution = solution
return {"clocking_option": self.clocking_option, "decimation": self.decimation}

def get_required_clock_names(self) -> List[str]:
Expand Down
121 changes: 121 additions & 0 deletions adijif/converters/ad9680_draw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
"""Drawing features for AD9680."""
from typing import Dict

from adijif.draw import Layout, Node # type: ignore # isort: skip # noqa: I202

class ad9680_draw:

def _init_diagram(self) -> None:
"""Initialize diagram for AD9680 alone."""
self.ic_diagram_node = None
self._diagram_output_dividers = []


self.ic_diagram_node = Node("AD9680")

# External
# ref_in = Node("REF_IN", ntype="input")
# lo.add_node(ref_in)

crossbar = Node("Crossbar", ntype="crossbar")
self.ic_diagram_node.add_child(crossbar)
for adc in range(2):
adc_node = Node(f"ADC{adc}", ntype="adc")
self.ic_diagram_node.add_child(adc_node)
adc_node.shape = "parallelogram"
self.ic_diagram_node.add_connection({"from": adc_node, "to": crossbar})

for ddc in range(4):
ddc_node = Node(f"DDC{ddc}", ntype="ddc")
self.ic_diagram_node.add_child(ddc_node)
self.ic_diagram_node.add_connection({"from": crossbar, "to": ddc_node})

jesd204_framer = Node("JESD204 Framer", ntype="jesd204framer")
self.ic_diagram_node.add_child(jesd204_framer)

for ddc in range(4):
ddc = self.ic_diagram_node.get_child(f"DDC{ddc}")
self.ic_diagram_node.add_connection({"from": ddc, "to": jesd204_framer})


def _update_diagram(self, config: Dict) -> None:
"""Update diagram with configuration.
Args:
config (Dict): Configuration dictionary
Raises:
Exception: If key is not D followed by a number
"""
# Add output dividers
keys = config.keys()
output_dividers = self.ic_diagram_node.get_child("Output Dividers")
for key in keys:
if key.startswith("D"):
div = Node(key, ntype="divider")
output_dividers.add_child(div)
self.ic_diagram_node.add_connection(
{"from": output_dividers, "to": div}
)
else:
raise Exception(
f"Unknown key {key}. Must be of for DX where X is a number"
)

def draw(self, clocks) -> str:
"""Draw diagram in d2 language for IC alone with reference clock.
Args:
clocks (Dict): Dictionary of clocks
Returns:
str: Diagram in d2 language
Raises:
Exception: If no solution is saved
"""
if not self._saved_solution:
raise Exception("No solution to draw. Must call solve first.")
lo = Layout("AD9680 Example")
lo.add_node(self.ic_diagram_node)

static_options = self.get_config()

ref_in = Node("REF_IN", ntype="input")
lo.add_node(ref_in)
for i in range(2):
adc = self.ic_diagram_node.get_child(f"ADC{i}")
lo.add_connection({"from": ref_in, "to": adc, "rate": clocks["ad9680_adc_clock"]})

# Update Node values
for ddc in range(4):
rate = clocks["ad9680_adc_clock"]
self.ic_diagram_node.update_connection("Crossbar", f"DDC{ddc}", rate)

ddc_node = self.ic_diagram_node.get_child(f"DDC{ddc}")
ddc_node.value = str(static_options["decimation"])
drate = rate/static_options["decimation"]

self.ic_diagram_node.update_connection(f"DDC{ddc}", "JESD204 Framer", drate)

# Connect clock to framer
sysref_in = Node("SYSREF_IN", ntype="input")

lo.add_connection(
{
"from": sysref_in,
"to": self.ic_diagram_node.get_child("JESD204 Framer"),
"rate": clocks["ad9680_sysref"],
}
)

# Connect Remote Deframer
remote_deframer = Node("JESD204 Deframer", ntype="deframer")

# Add connect for each lane
for i in range(self.L):
lane_rate = self.bit_clock
lo.add_connection({"from": self.ic_diagram_node.get_child("JESD204 Framer"), "to": remote_deframer, "rate": lane_rate})


return lo.draw()
4 changes: 2 additions & 2 deletions adijif/jesd.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,12 @@ def _check_jesd_config(self) -> None:
if "jesd204c" in self.available_jesd_modes:
if self.bit_clock > 32e9:
raise Exception(
"bit clock (lane rate) {self.bit_clock} too high for JESD204C"
f"bit clock (lane rate) {self.bit_clock} too high for JESD204C"
)
elif "jesd204b" in self.available_jesd_modes:
if self.bit_clock > 12.5e9:
raise Exception(
"bit clock (lane rate) {self.bit_clock} too high for JESD204B"
f"bit clock (lane rate) {self.bit_clock} too high for JESD204B"
)
else:
raise Exception(f"JESD mode(s) {self.available_jesd_modes}")
Expand Down
1 change: 1 addition & 0 deletions adijif/solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from docplex.cp.model import binary_var # type: ignore
from docplex.cp.model import CpoModel, integer_var, interval_var # type: ignore
from docplex.cp.solution import CpoSolveResult # type: ignore
from docplex.cp.modeler import constant # type: ignore

cplex_solver = True
except ImportError:
Expand Down
61 changes: 61 additions & 0 deletions adijif/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,64 @@ def __call__(self, model: Union[GEKKO, CpoModel]) -> Dict:
config["range"] = model.Intermediate(model.sum(o_array * options))

return config

class arb_source:
"""Arbitrary source for solver.
This internally uses a division of two integers to create an arbitrary clock
source.
"""

_max_scalar = int(1e11)

def __init__(self, name):
"""Arbitrary source for solver.
Args:
name (str): Name of source
"""
self.name = name

self._a = integer_var(0, self._max_scalar, name=name + "_a")
self._b = integer_var(0, self._max_scalar, name=name + "_b")

def __call__(self, model: Union[GEKKO, CpoModel]) -> Dict:
"""Generate arbitrary source for solver.
Args:
model (GEKKO, CpoModel): Model of JESD system or part to solve
Returns:
Dict: Dictionary of solver variable(s) for model
"""
if GEKKO and CpoModel:
assert isinstance(
model, (GEKKO, CpoModel)
), "arb_source must be called with input type model"
elif GEKKO:
assert isinstance(
model, GEKKO
), "arb_source must be called with input type model"
elif CpoModel:
assert isinstance(
model, CpoModel
), "arb_source must be called with input type model"

config = {}
if isinstance(model, CpoModel):
# config[self.name] = self._a / self._b
# return config
return self._a / self._b

raise NotImplementedError("Only CpoModel is supported")

def get_config(self, solution: Dict) -> Dict:
"""Get configuration from solver results.
Args:
solution (Dict): Solver results
Returns:
Dict: Dictionary of solver variable(s) for model
"""
return {self.name: solution[self._a] / solution[self._b]}

0 comments on commit efe318f

Please sign in to comment.