|
8 | 8 | from adijif.plls.pll import pll |
9 | 9 | from adijif.solvers import CpoExpr, GK_Intermediate |
10 | 10 |
|
| 11 | +from adijif.draw import Layout, Node |
| 12 | + |
| 13 | + |
| 14 | +class adf4030_drawer(object): |
| 15 | + """ADF4030 diagram drawer.""" |
| 16 | + |
| 17 | + def _init_diagram(self) -> None: |
| 18 | + """Initialize diagram with PLL block.""" |
| 19 | + |
| 20 | + self._diagram_output_dividers = [] |
| 21 | + |
| 22 | + self.ic_diagram_node = Node("ADF4030") |
| 23 | + |
| 24 | + rdiv = Node("R", ntype="divider") |
| 25 | + self.ic_diagram_node.add_child(rdiv) |
| 26 | + |
| 27 | + pfd = Node("PFD", ntype="phase-frequency-detector") |
| 28 | + self.ic_diagram_node.add_child(pfd) |
| 29 | + |
| 30 | + charge_pump = Node("CP", ntype="charge-pump") |
| 31 | + self.ic_diagram_node.add_child(charge_pump) |
| 32 | + |
| 33 | + loop_filter = Node("LF", ntype="loop-filter") |
| 34 | + self.ic_diagram_node.add_child(loop_filter) |
| 35 | + |
| 36 | + vco = Node("VCO", ntype="vco") |
| 37 | + self.ic_diagram_node.add_child(vco) |
| 38 | + |
| 39 | + ndiv = Node("N", ntype="divider") |
| 40 | + self.ic_diagram_node.add_child(ndiv) |
| 41 | + |
| 42 | + output_dividers = Node("Output Dividers", ntype="shell") |
| 43 | + self.ic_diagram_node.add_child(output_dividers) |
| 44 | + |
| 45 | + # Connections |
| 46 | + self.ic_diagram_node.add_connection({ |
| 47 | + "from": rdiv, |
| 48 | + "to": pfd, |
| 49 | + }) |
| 50 | + self.ic_diagram_node.add_connection({ |
| 51 | + "from": pfd, |
| 52 | + "to": charge_pump, |
| 53 | + }) |
| 54 | + self.ic_diagram_node.add_connection({ |
| 55 | + "from": charge_pump, |
| 56 | + "to": loop_filter, |
| 57 | + }) |
| 58 | + self.ic_diagram_node.add_connection({ |
| 59 | + "from": loop_filter, |
| 60 | + "to": vco, |
| 61 | + }) |
| 62 | + self.ic_diagram_node.add_connection({ |
| 63 | + "from": vco, |
| 64 | + "to": ndiv, |
| 65 | + }) |
| 66 | + self.ic_diagram_node.add_connection({ |
| 67 | + "from": ndiv, |
| 68 | + "to": pfd, |
| 69 | + }) |
| 70 | + self.ic_diagram_node.add_connection({ |
| 71 | + "from": vco, |
| 72 | + "to": output_dividers, |
| 73 | + }) |
| 74 | + |
| 75 | + def _update_diagram(self, config: Dict) -> None: |
| 76 | + """Update diagram with new dividers.""" |
| 77 | + |
| 78 | + keys = config.keys() |
| 79 | + output_dividers = self.ic_diagram_node.get_child("Output Dividers") |
| 80 | + for key in keys: |
| 81 | + if "o_div" in key and key not in self._diagram_output_dividers: |
| 82 | + od_node = Node(key, ntype="divider") |
| 83 | + output_dividers.add_child(od_node) |
| 84 | + self.ic_diagram_node.add_connection({ |
| 85 | + "from": output_dividers, |
| 86 | + "to": od_node, |
| 87 | + }) |
| 88 | + else: |
| 89 | + raise Exception("Unexpected config key: {}".format(key)) |
| 90 | + |
| 91 | + def draw(self, lo: Layout = None) -> Union[str, Layout]: |
| 92 | + """Draw diagram with configuration. |
| 93 | +
|
| 94 | + Args: |
| 95 | + lo (Layout): Diagram layout object |
| 96 | +
|
| 97 | + Returns: |
| 98 | + Layout: Diagram layout object |
| 99 | + """ |
| 100 | + if not self._saved_solution: |
| 101 | + raise Exception("No solution to draw. Must call solve first") |
| 102 | + |
| 103 | + system_draw = lo is not None |
| 104 | + if not system_draw: |
| 105 | + lo = Layout("ADF4030 Diagram") |
| 106 | + else: |
| 107 | + assert isinstance(lo, Layout), "Layout object must be provided for system drawing" |
| 108 | + lo.add_node(self.ic_diagram_node) |
| 109 | + |
| 110 | + ref_in = Node("REF_IN", ntype="input") |
| 111 | + rdiv = self.ic_diagram_node.get_child("R") |
| 112 | + lo.add_connection({ |
| 113 | + "from": ref_in, |
| 114 | + "to": rdiv, |
| 115 | + "rate": 100e6, # TODO: Get actual rate |
| 116 | + }) |
| 117 | + |
| 118 | + # Update node values |
| 119 | + node = self.ic_diagram_node.get_child("R") |
| 120 | + node.value = str(self._saved_solution["r"]) |
| 121 | + node = self.ic_diagram_node.get_child("N") |
| 122 | + node.value = str(self._saved_solution["n"]) |
| 123 | + # for clk in self._clk_names: |
| 124 | + # od_node = self.ic_diagram_node.get_child(f"o_div_{clk}_adf4030") |
| 125 | + # od_node.value = str(self._saved_solution[f"o_div_{clk}_adf4030"]) |
| 126 | + for key, val in self._saved_solution["output_clocks"].items(): |
| 127 | + div = Node(key, ntype="divider") |
| 128 | + div.value = str(val["divider"]) |
| 129 | + lo.add_node(div) |
| 130 | + lo.add_connection({ |
| 131 | + "from": self.ic_diagram_node.get_child("Output Dividers"), |
| 132 | + "to": div, |
| 133 | + "rate": self._saved_solution["vco"] / val["divider"], |
| 134 | + }) |
| 135 | + |
| 136 | + if system_draw: |
| 137 | + return lo.draw() |
| 138 | + |
| 139 | + return lo.draw() |
| 140 | + |
| 141 | + |
| 142 | +class adf4030(pll, adf4030_drawer): |
11 | 143 |
|
12 | | -class adf4030(pll): |
13 | 144 | """ADF4030 PLL model. |
14 | 145 |
|
15 | 146 | This model currently supports all divider configurations |
@@ -157,6 +288,8 @@ def get_config(self, solution: CpoSolveResult = None) -> Dict: |
157 | 288 |
|
158 | 289 | config["output_clocks"] = output_config |
159 | 290 |
|
| 291 | + self._saved_solution = config |
| 292 | + |
160 | 293 | return config |
161 | 294 |
|
162 | 295 | def _setup_solver_constraints( |
@@ -322,3 +455,4 @@ def set_requested_clocks( |
322 | 455 | self._add_equation( |
323 | 456 | self.config[o_div_name] * out_freq[i] == self.config["vco"], |
324 | 457 | ) |
| 458 | + |
0 commit comments