From d2f6bb1f822600dcae62da6b3e8e74fe9f443b99 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Fri, 14 Jun 2024 14:13:43 -0600 Subject: [PATCH] Add system draw features. This WIP as it breaks standalone drawing Signed-off-by: Travis F. Collins --- adijif/clocks/hmc7044.py | 17 +- adijif/converters/ad9680_draw.py | 72 +++++++-- adijif/draw.py | 140 ++++++++++++++-- adijif/fpgas/xilinx.py | 8 +- adijif/fpgas/xilinx_draw.py | 265 +++++++++++++++++++++++++++++++ adijif/gekko_trans.py | 2 + adijif/system.py | 3 +- adijif/system_draw.py | 48 ++++++ examples/ad9680_draw.py | 34 ++++ examples/xilinx_draw.py | 38 +++++ 10 files changed, 597 insertions(+), 30 deletions(-) create mode 100644 adijif/fpgas/xilinx_draw.py create mode 100644 adijif/system_draw.py create mode 100644 examples/ad9680_draw.py create mode 100644 examples/xilinx_draw.py diff --git a/adijif/clocks/hmc7044.py b/adijif/clocks/hmc7044.py index 2e31510..7378558 100644 --- a/adijif/clocks/hmc7044.py +++ b/adijif/clocks/hmc7044.py @@ -247,7 +247,7 @@ def _update_diagram(self, config: Dict) -> None: f"Unknown key {key}. Must be of for DX where X is a number" ) - def draw(self) -> str: + def draw(self, lo = None) -> str: """Draw diagram in d2 language for IC alone with reference clock. Returns: @@ -258,7 +258,13 @@ def draw(self) -> str: """ if not self._saved_solution: raise Exception("No solution to draw. Must call solve first.") - lo = Layout("HMC7044 Example") + + system_draw = lo is not None + if not system_draw: + lo = Layout("HMC7044 Example") + else: + # Verify lo is a Layout object + assert isinstance(lo, Layout), "lo must be a Layout object" lo.add_node(self.ic_diagram_node) ref_in = Node("REF_IN", ntype="input") @@ -309,7 +315,8 @@ def draw(self) -> str: lo.add_node(clk_node) lo.add_connection({"from": div, "to": clk_node, "rate": val["rate"]}) - return lo.draw() + if system_draw: + return lo.draw() def get_config(self, solution: CpoSolveResult = None) -> Dict: """Extract configurations from solver results. @@ -468,6 +475,10 @@ def _get_clock_constraint( else: raise Exception("Unknown solver {}".format(self.solver)) + # Update diagram to include new divider + d_n = len(self.config["out_dividers"]) + self._update_diagram({f"D{d_n}": od}) + self.config["out_dividers"].append(od) return self.config["vcxod"] / self.config["r2"] * self.config["n2"] / od diff --git a/adijif/converters/ad9680_draw.py b/adijif/converters/ad9680_draw.py index 717ba69..573efdf 100644 --- a/adijif/converters/ad9680_draw.py +++ b/adijif/converters/ad9680_draw.py @@ -5,6 +5,8 @@ class ad9680_draw: + _system_draw = False + def _init_diagram(self) -> None: """Initialize diagram for AD9680 alone.""" self.ic_diagram_node = None @@ -62,7 +64,7 @@ def _update_diagram(self, config: Dict) -> None: f"Unknown key {key}. Must be of for DX where X is a number" ) - def draw(self, clocks) -> str: + def draw(self, clocks, lo=None, clock_chip_node=None) -> str: """Draw diagram in d2 language for IC alone with reference clock. Args: @@ -76,20 +78,39 @@ def draw(self, clocks) -> str: """ if not self._saved_solution: raise Exception("No solution to draw. Must call solve first.") - lo = Layout("AD9680 Example") + + system_draw = lo is not None + + if not system_draw: + lo = Layout("AD9680 Example") + else: + # Verify lo is a Layout object + assert isinstance(lo, Layout), "lo must be a Layout object" lo.add_node(self.ic_diagram_node) static_options = self.get_config() - ref_in = Node("REF_IN", ntype="input") + if not system_draw: + ref_in = Node("REF_IN", ntype="input") + else: + to_node = lo.get_node("AD9680_ref_clk") + # Locate node connected to this one + from_node = lo.get_connection(to=to_node.name) + assert from_node, "No connection found" + assert isinstance(from_node, list), "Connection must be a list" + assert len(from_node) == 1, "Only one connection allowed" + ref_in = from_node[0]["from"] + # Remove to_node since it is not needed + lo.remove_node(to_node.name) + 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"]}) + lo.add_connection({"from": ref_in, "to": adc, "rate": clocks["AD9680_ref_clk"]}) # Update Node values for ddc in range(4): - rate = clocks["ad9680_adc_clock"] + rate = clocks["AD9680_ref_clk"] self.ic_diagram_node.update_connection("Crossbar", f"DDC{ddc}", rate) ddc_node = self.ic_diagram_node.get_child(f"DDC{ddc}") @@ -99,15 +120,34 @@ def draw(self, clocks) -> str: 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"], - } - ) + if not system_draw: + 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"], + } + ) + else: + to_node = lo.get_node("AD9680_sysref") + # Locate node connected to this one + from_node = lo.get_connection(to=to_node.name) + assert from_node, "No connection found" + assert isinstance(from_node, list), "Connection must be a list" + assert len(from_node) == 1, "Only one connection allowed" + sysref_in = from_node[0]["from"] + # Remove to_node since it is not needed + lo.remove_node(to_node.name) + + 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") @@ -117,5 +157,5 @@ def draw(self, clocks) -> str: 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() \ No newline at end of file + if not system_draw: + return lo.draw() \ No newline at end of file diff --git a/adijif/draw.py b/adijif/draw.py index 1ad9a11..88749ef 100644 --- a/adijif/draw.py +++ b/adijif/draw.py @@ -135,6 +135,30 @@ def get_child(self, name: str) -> Node: if child.name == name: return child raise ValueError(f"Child with name {name} not found.") + + def remove_child(self, name: str) -> None: + """Remove child node by name. + + Args: + name (str): Name of the child node to remove. + """ + # Remove connections with the child first + connections_to_keep = [] + for conn in self.connections: + if conn["to"].name != name: + connections_to_keep.append(conn) + self.connections = connections_to_keep + connections_to_keep = [] + for conn in self.connections: + if conn["from"].name != name: + connections_to_keep.append(conn) + self.connections = connections_to_keep + + children_to_keep = [] + for child in self.children: + if child.name != name: + children_to_keep.append(child) + self.children = children_to_keep class Layout: @@ -142,6 +166,7 @@ class Layout: si = " " use_d2_cli = False + _write_out_d2_file = True def __init__(self, name: str) -> None: """Initialize layout with name. @@ -165,6 +190,30 @@ def add_node(self, node: Node) -> None: """ self.nodes.append(node) + def remove_node(self, name: str) -> None: + """Remove node by name. + + Args: + name (str): Name of the node to remove. + """ + # Remove connections with the node first + connections_to_keep = [] + for conn in self.connections: + if conn["to"].name != name: + connections_to_keep.append(conn) + self.connections = connections_to_keep + connections_to_keep = [] + for conn in self.connections: + if conn["from"].name != name: + connections_to_keep.append(conn) + self.connections = connections_to_keep + + nodes_to_keep = [] + for node in self.nodes: + if node.name != name: + nodes_to_keep.append(node) + self.nodes = nodes_to_keep + def add_connection(self, connection: dict) -> None: """Add connection between two nodes. @@ -183,6 +232,54 @@ def add_connection(self, connection: dict) -> None: rate /= 1000 self.connections.append(connection) + def get_connection(self, from_s: str = None, to: str = None) -> dict: + """Get connection between two nodes. + + Args: + from_s (str): Name of the node from which connection originates. + to (str): Name of the node to which connection goes. + + Returns: + List[dict] or dict: List of or single Connection dictionary with keys "from", "to" and optionally "rate". + + Raises: + ValueError: If connection not found. + """ + if from_s is None and to is None: + raise ValueError("Both from and to cannot be None.") + found = [] + if from_s is None: + for conn in self.connections: + if conn["to"].name == to: + found.append(conn) + return found + if to is None: + for conn in self.connections: + if conn["from"].name == from_s: + found.append(conn) + return found + for conn in self.connections: + if conn["from"].name == from_s and conn["to"].name == to: + return conn + + + def get_node(self, name: str) -> Node: + """Get node by name. + + Args: + name (str): Name of the node. + + Returns: + Node: Node with the given name. + + Raises: + ValueError: If node not found. + """ + for node in self.nodes: + if node.name == name: + return node + raise ValueError(f"Node with name {name} not found.") + def draw(self) -> str: """Draw diagram in d2 language. @@ -259,15 +356,35 @@ def draw_subnodes(node: Node, spacing: str = " ") -> str: diag += ": " + label if label else "" diag += "\n" - for node in self.nodes: - for connection in node.connections: - from_p_name = get_parents_names(connection["from"]) - to_p_name = get_parents_names(connection["to"]) - label = f"{connection['rate']}" if "rate" in connection else "" - diag += f"{from_p_name}{connection['from'].name} -> " - diag += f"{to_p_name}{connection['to'].name}" - diag += ": " + label if label else "" - diag += "\n" + def draw_nodes_connections(nodes): + diag = "" + for node in nodes: + for connection in node.connections: + from_p_name = get_parents_names(connection["from"]) + to_p_name = get_parents_names(connection["to"]) + label = f"{connection['rate']}" if "rate" in connection else "" + diag += f"{from_p_name}{connection['from'].name} -> " + diag += f"{to_p_name}{connection['to'].name}" + diag += ": " + label if label else "" + diag += "\n" + + if node.children: + diag += draw_nodes_connections(node.children) + + return diag + + # for node in self.nodes: + diag += draw_nodes_connections(self.nodes) + + # for node in self.nodes: + # for connection in node.connections: + # from_p_name = get_parents_names(connection["from"]) + # to_p_name = get_parents_names(connection["to"]) + # label = f"{connection['rate']}" if "rate" in connection else "" + # diag += f"{from_p_name}{connection['from'].name} -> " + # diag += f"{to_p_name}{connection['to'].name}" + # diag += ": " + label if label else "" + # diag += "\n" if self.use_d2_cli: with open(self.output_filename, "w") as f: @@ -278,6 +395,11 @@ def draw_subnodes(node: Node, spacing: str = " ") -> str: os.system(cmd) # noqa: S605 return self.output_image_filename else: + if self._write_out_d2_file: + with open(self.output_filename, "w") as f: + f.write(diag) + print(f"Saved to {self.output_filename}") + # Use bindings from .d2 import compile out = compile(diag) diff --git a/adijif/fpgas/xilinx.py b/adijif/fpgas/xilinx.py index 0808d90..545ada4 100644 --- a/adijif/fpgas/xilinx.py +++ b/adijif/fpgas/xilinx.py @@ -3,12 +3,13 @@ from adijif.converters.converter import converter as conv from adijif.fpgas.xilinx_bf import xilinx_bf +from adijif.fpgas.xilinx_draw import xilinx_draw from adijif.solvers import CpoSolveResult # type: ignore from adijif.solvers import integer_var # type: ignore from adijif.solvers import CpoIntVar, GK_Intermediate, GK_Operators, GKVariable -class xilinx(xilinx_bf): +class xilinx(xilinx_bf, xilinx_draw): """Xilinx FPGA clocking model. This model captures different limitations of the Xilinx @@ -16,6 +17,7 @@ class xilinx(xilinx_bf): Currently only Zynq 7000 devices have been fully tested. """ + name = "Xilinx-FPGA" favor_cpll_over_qpll = False minimize_fpga_ref_clock = False @@ -432,6 +434,7 @@ def setup_by_dev_kit_name(self, name: str) -> None: self.max_serdes_lanes = 24 else: raise Exception(f"No boardname found in library for {name}") + self.name = name def determine_pll(self, bit_clock: int, fpga_ref_clock: int) -> Dict: """Determin if configuration is possible with CPLL or QPLL. @@ -500,6 +503,8 @@ def get_config( if solution: self.solution = solution + self._saved_solution = solution + for config in self.configs: pll_config: Dict[str, Union[str, int, float]] = {} @@ -526,6 +531,7 @@ def get_config( fpga_ref * pll_config["n1"] * pll_config["n2"] / pll_config["m"] # type: ignore # noqa: B950 ) # Check + print(converter.bit_clock, pll_config["d"], pll_config["vco"]) assert ( pll_config["vco"] * 2 / pll_config["d"] == converter.bit_clock # type: ignore # noqa: B950 ), "Invalid CPLL lane rate" diff --git a/adijif/fpgas/xilinx_draw.py b/adijif/fpgas/xilinx_draw.py new file mode 100644 index 0000000..82a9872 --- /dev/null +++ b/adijif/fpgas/xilinx_draw.py @@ -0,0 +1,265 @@ +"""Drawing features for Xilinx FPGA designs.""" +from typing import Dict, List, Tuple + +from adijif.draw import Layout, Node + + +class xilinx_draw: + def _init_diagram(self) -> None: + """Initialize the diagram for a Xilinx FPGA alone.""" + self.ic_diagram_node = None + + self.ic_diagram_node = Node(self.name) + + # Add generic transceiver since we don't know what is used until later + transceiver = Node("Transceiver", ntype="transceiver") + self.ic_diagram_node.add_child(transceiver) + + # Add Link layer JESD204 core + jesd204_link = Node("JESD204-Link-IP", ntype="ip") + self.ic_diagram_node.add_child(jesd204_link) + + # Add Transport Layer JESD204 core + jesd204_transport = Node("JESD204-Transport-IP", ntype="ip") + self.ic_diagram_node.add_child(jesd204_transport) + + # Add Application layer JESD204 core + jesd204_application = Node("JESD204-Application-IP", ntype="ip") + self.ic_diagram_node.add_child(jesd204_application) + + # Add connections + self.ic_diagram_node.add_connection({"from": transceiver, "to": jesd204_link}) + self.ic_diagram_node.add_connection( + {"from": jesd204_link, "to": jesd204_transport} + ) + self.ic_diagram_node.add_connection( + {"from": jesd204_transport, "to": jesd204_application} + ) + + def _draw_phy(self, config: Dict) -> None: + + cfg = { + "clocks": {"FPGA_REF": 500000000.0, "LINK_OUT_REF": 125000000.0}, + "fpga": { + "type": "cpll", + "m": 2, + "d": 1, + "n1": 5, + "n2": 4, + "vco": 5000000000.0, + "sys_clk_select": "XCVR_CPLL", + "progdiv": 40.0, + "out_clk_select": "XCVR_PROGDIV_CLK", + "separate_device_clock_required": 1, + "transport_samples_per_clock": 8, + }, + } + + phy = Node("JESD204-PHY-IP", ntype="phy") + self.ic_diagram_node.add_child(phy) + + # PLL + if config['fpga']['type'] == 'cpll': + cpll = Node("CPLL", ntype="cpll") + phy.add_child(cpll) + + # Put stuff in CPLL + m = Node("M", ntype="divider") + cpll.add_child(m) + m.value = config['fpga']['m'] + + pfd = Node("PFD", ntype="phase-frequency-detector") + cpll.add_child(pfd) + cpll.add_connection({"from": m, "to": pfd, "rate": config['clocks']['FPGA_REF']}) + + cp = Node("CP", ntype="charge-pump") + cpll.add_child(cp) + cpll.add_connection({"from": pfd, "to": cp}) + + lpf = Node("LPF", ntype="loop-filter") + cpll.add_child(lpf) + cpll.add_connection({"from": cp, "to": lpf}) + + vco = Node("VCO", ntype="vco") + cpll.add_child(vco) + cpll.add_connection({"from": lpf, "to": vco}) + + d = Node("D", ntype="divider") + cpll.add_child(d) + d.value = config['fpga']['d'] + cpll.add_connection({"from": vco, "to": d, "rate": config['fpga']['vco']}) + + n1 = Node("N1", ntype="divider") + cpll.add_child(n1) + n1.value = config['fpga']['n1'] + cpll.add_connection({"from": vco, "to": n1}) + + n2 = Node("N2", ntype="divider") + cpll.add_child(n2) + n2.value = config['fpga']['n2'] + cpll.add_connection({"from": n1, "to": n2}) + cpll.add_connection({"from": n2, "to": pfd}) + + transceiver = Node("Transceiver", ntype="transceiver") + phy.add_child(transceiver) + phy.add_connection({"from": d, "to": transceiver}) + + + in_c = m + xcvr_out = d + xcvr_out_rate = config['fpga']['vco'] / config['fpga']['d'] + + else: + + qpll = Node("QPLL", ntype="qpll") + phy.add_child(qpll) + + xcvr_out = qpll + xcvr_out_rate = config['fpga']['vco'] + + in_c = qpll + + + # Divider complex + trx_dividers = Node("Transceiver Dividers", ntype="trx-dividers") + phy.add_child(trx_dividers) + + if config['fpga']['out_clk_select'] == "XCVR_OUTCLK_PCS": + raise Exception("Only XCVR_PROGDIV_CLK supported for now") + elif config['fpga']['out_clk_select'] == "XCVR_OUTCLK_PMA": + raise Exception("Only XCVR_PROGDIV_CLK supported for now") + elif config['fpga']['out_clk_select'] == "XCVR_REF_CLK": + # raise Exception("Only XCVR_PROGDIV_CLK supported") + + div_ref_clk = Node("DIV_REF_CLK", ntype="divider") + trx_dividers.add_child(div_ref_clk) + div_ref_clk.value = 1 + trx_dividers.add_connection({"from": xcvr_out, "to": div_ref_clk, "rate": xcvr_out_rate}) + + out_rate = xcvr_out_rate + out = div_ref_clk + + elif config['fpga']['out_clk_select'] == "XCVR_REFCLK_DIV2": + raise Exception("Only XCVR_PROGDIV_CLK supported") + + elif config['fpga']['out_clk_select'] == "XCVR_PROGDIV_CLK": + + mux = Node("PLLCLKSEL-Mux", ntype="mux") + mux.value = config['fpga']['type'] + trx_dividers.add_child(mux) + phy.add_connection({"from": xcvr_out, "to": mux, "rate": xcvr_out_rate}) + + cdr = Node("CDR", ntype="cdr") + trx_dividers.add_child(cdr) + trx_dividers.add_connection({"from": mux, "to": cdr}) + + progdiv = Node("ProgDiv", ntype="divider") + trx_dividers.add_child(progdiv) + progdiv.value = int(config['fpga']['progdiv']) + trx_dividers.add_connection({"from": cdr, "to": progdiv}) + + out_rate = xcvr_out_rate / config['fpga']['progdiv'] + out = progdiv + else: + raise Exception(f"Unknown out_clk_select: {config['fpga']['out_clk_select']}") + + + out_mux = Node("OUTCLKSEL-Mux", ntype="mux") + trx_dividers.add_child(out_mux) + trx_dividers.add_connection({"from": out, "to": out_mux, "rate": out_rate}) + + return in_c, out_mux + + def draw(self, config, lo=None) -> str: + """Draw diagram in d2 language for IC alone with reference clock. + + Args: + clocks (Dict): Clock settings + + Returns: + str: SVG data + """ + if not self._saved_solution: + raise Exception("No solution to draw. Must call solve first.") + + system_draw = lo is not None + + if not system_draw: + lo = Layout(f"{self.name} Example") + else: + # Verify lo is a Layout object + assert isinstance(lo, Layout), "lo must be a Layout object" + + lo.add_node(self.ic_diagram_node) + + clocks = config["clocks"] + + if not system_draw: + ref_in = Node("REF_IN", ntype="input") + else: + to_node = lo.get_node("AD9680_fpga_ref_clk") + # Locate node connected to this one + from_node = lo.get_connection(to=to_node.name) + assert from_node, "No connection found" + assert isinstance(from_node, list), "Connection must be a list" + ref_in = from_node[0]["from"] + lo.remove_node(to_node.name) + lo.add_node(ref_in) + + in_c, out_c = self._draw_phy(config) + self.ic_diagram_node.add_connection({"from": ref_in, "to": in_c, "rate": clocks["AD9680_ref_clk"]}) + # Delete Transceiver node + self.ic_diagram_node.remove_child("Transceiver") + + # Connect out_c to JESD204-Link-IP + self.ic_diagram_node.add_connection( + { + "from": out_c, + "to": self.ic_diagram_node.get_child("JESD204-Link-IP"), + "rate": clocks["AD9680_fpga_link_out_clk"], + } + ) + + # Connect device clock to JESD204-Link-IP + if not system_draw: + device_clock = Node("Device Clock", ntype="input") + else: + to_node = lo.get_node("AD9680_fpga_link_out_clk") + # Locate node connected to this one + from_node = lo.get_connection(to=to_node.name) + assert from_node, "No connection found" + assert isinstance(from_node, list), "Connection must be a list" + device_clock = from_node[0]["from"] + lo.remove_node(to_node.name) + lo.add_node(device_clock) + self.ic_diagram_node.add_connection( + { + "from": device_clock, + "to": self.ic_diagram_node.get_child("JESD204-Link-IP"), + "rate": clocks["AD9680_fpga_link_out_clk"], + } + ) + + # Connect SYSREF to JESD204-Link-IP + if not system_draw: + sysref = Node("SYSREF", ntype="input") + else: + parent = lo.get_node("AD9680") + to_node = parent.get_child("JESD204 Framer") + # Locate node connected to this one + from_node = lo.get_connection(to=to_node.name) + assert from_node, "No connection found" + assert isinstance(from_node, list), "Connection must be a list" + sysref = from_node[0]["from"] + # lo.remove_node(to_node.name) + lo.add_node(sysref) + self.ic_diagram_node.add_connection( + { + "from": sysref, + "to": self.ic_diagram_node.get_child("JESD204-Link-IP"), + } + ) + + # Update with config settings + + return lo.draw() diff --git a/adijif/gekko_trans.py b/adijif/gekko_trans.py index c3dda17..8936889 100644 --- a/adijif/gekko_trans.py +++ b/adijif/gekko_trans.py @@ -102,6 +102,8 @@ def _get_val( elif self.solver == "CPLEX": if isinstance(value, (int, float)): return value + # print(self.warm_start) + # self.solution.write() return self.solution.get_value(value.get_name()) else: raise Exception(f"Unknown solver {self.solver}") diff --git a/adijif/system.py b/adijif/system.py index c712a1e..06bdc8b 100644 --- a/adijif/system.py +++ b/adijif/system.py @@ -11,9 +11,10 @@ from adijif.converters.converter import converter as convc from adijif.plls.pll import pll as pllc from adijif.types import range as rangec +from adijif.system_draw import system_draw # noqa: F401 -class system: +class system(system_draw): """System Manager Class. Manage requirements from all system components and feed into clock rate diff --git a/adijif/system_draw.py b/adijif/system_draw.py new file mode 100644 index 0000000..01e32c6 --- /dev/null +++ b/adijif/system_draw.py @@ -0,0 +1,48 @@ +"""Diagram drawing for System level.""" +from typing import Dict + +from adijif.draw import Layout, Node # type: ignore # isort: skip # noqa: I202 + +class system_draw: + + def _init_diagram(self) -> None: + """Initialize diagram for system.""" + self.ic_diagram_node = None + + self.ic_diagram_node = Node("System") + + def draw(self, config): + + lo = Layout("System Diagram") + self._init_diagram() + + self.ic_diagram_node.add_child(lo) + + # Clocking + assert self.clock is not None + assert not isinstance(self.clock, list), "Only one clocking supported" + + self.clock.draw(lo) + + # Converter + assert self.converter is not None + assert not isinstance(self.converter, list), "Only one converter supported" + + cnv_clocking = config["clock"]['output_clocks'].copy() + for clk in cnv_clocking: + rate = cnv_clocking[clk]['rate'] + cnv_clocking[clk] = rate + + self.converter.draw(cnv_clocking, lo, self.clock.ic_diagram_node) + + # FPGA + assert self.fpga is not None + assert not isinstance(self.fpga, list), "Only one FPGA supported" + + print(config) + fpga_clocking = {"clocks": cnv_clocking, 'fpga': config['fpga_AD9680']} + self.fpga.draw(fpga_clocking, lo) + + # Draw the diagram + return lo.draw() + diff --git a/examples/ad9680_draw.py b/examples/ad9680_draw.py new file mode 100644 index 0000000..38897c8 --- /dev/null +++ b/examples/ad9680_draw.py @@ -0,0 +1,34 @@ +import adijif as jif + + +adc = jif.ad9680() + +# Check static +adc.validate_config() + +required_clocks = adc.get_required_clocks() +required_clock_names = adc.get_required_clock_names() + +# Add generic clock sources for solver +clks = [] +for clock, name in zip(required_clocks, required_clock_names): + clk = jif.types.arb_source(name) + adc._add_equation(clk(adc.model) == clock) + clks.append(clk) + +# Solve +solution = adc.model.solve(LogVerbosity="Quiet") +settings = adc.get_config(solution) + +# Get clock values +clock_values = {} +for clk in clks: + clock_values.update(clk.get_config(solution)) +settings["clocks"] = clock_values + +print(settings) + +image_data = adc.draw(settings["clocks"]) + +with open("ad9680_example.svg", "w") as f: + f.write(image_data) \ No newline at end of file diff --git a/examples/xilinx_draw.py b/examples/xilinx_draw.py new file mode 100644 index 0000000..9922e6f --- /dev/null +++ b/examples/xilinx_draw.py @@ -0,0 +1,38 @@ +import adijif as jif +from adijif.converters.converter import converter + +fpga = jif.xilinx() +fpga.setup_by_dev_kit_name("vcu118") + +# class dummy_converter(converter): +# name = "dummy" + +# dc = dummy_converter() +dc = jif.ad9680() + + +fpga_ref = jif.types.arb_source("FPGA_REF") +link_out_ref = jif.types.arb_source("LINK_OUT_REF") + +clocks = fpga.get_required_clocks(dc, fpga_ref(fpga.model), link_out_ref(fpga.model)) +print(clocks) + +solution = fpga.model.solve(LogVerbosity="Quiet") +solution.write() + +settings = {} +# Get clock values +clock_values = {} +for clk in [fpga_ref, link_out_ref]: + clock_values.update(clk.get_config(solution)) +settings["clocks"] = clock_values + + +settings['fpga'] = fpga.get_config(dc, settings['clocks']['FPGA_REF'], solution) +print(settings) + + +image_data = fpga.draw(settings) + +with open("xilinx_example.svg", "w") as f: + f.write(image_data) \ No newline at end of file