pyadi-jif 0.1.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- adijif/__init__.py +32 -0
- adijif/adijif.py +1 -0
- adijif/cli.py +21 -0
- adijif/clocks/__init__.py +10 -0
- adijif/clocks/ad9523.py +321 -0
- adijif/clocks/ad9523_1_bf.py +91 -0
- adijif/clocks/ad9528.py +444 -0
- adijif/clocks/ad9528_bf.py +70 -0
- adijif/clocks/ad9545.py +553 -0
- adijif/clocks/clock.py +153 -0
- adijif/clocks/hmc7044.py +558 -0
- adijif/clocks/hmc7044_bf.py +68 -0
- adijif/clocks/ltc6952.py +624 -0
- adijif/clocks/ltc6952_bf.py +67 -0
- adijif/clocks/ltc6953.py +509 -0
- adijif/common.py +70 -0
- adijif/converters/__init__.py +3 -0
- adijif/converters/ad9081.py +679 -0
- adijif/converters/ad9081_dp.py +206 -0
- adijif/converters/ad9081_util.py +124 -0
- adijif/converters/ad9084.py +588 -0
- adijif/converters/ad9084_dp.py +111 -0
- adijif/converters/ad9084_draw.py +203 -0
- adijif/converters/ad9084_util.py +365 -0
- adijif/converters/ad9144.py +316 -0
- adijif/converters/ad9144_bf.py +44 -0
- adijif/converters/ad9680.py +201 -0
- adijif/converters/ad9680_bf.py +43 -0
- adijif/converters/ad9680_draw.py +184 -0
- adijif/converters/adc.py +83 -0
- adijif/converters/adrv9009.py +426 -0
- adijif/converters/adrv9009_bf.py +43 -0
- adijif/converters/adrv9009_util.py +89 -0
- adijif/converters/converter.py +399 -0
- adijif/converters/dac.py +85 -0
- adijif/converters/resources/AD9084_JTX_JRX.xlsx +0 -0
- adijif/converters/resources/ad9081_JRx_204B.csv +180 -0
- adijif/converters/resources/ad9081_JRx_204C.csv +411 -0
- adijif/converters/resources/ad9081_JTx_204B.csv +1488 -0
- adijif/converters/resources/ad9081_JTx_204C.csv +1064 -0
- adijif/converters/resources/full_rx_mode_table_ad9081.csv +1904 -0
- adijif/converters/resources/full_tx_mode_table_ad9081.csv +994 -0
- adijif/d2/__init__.py +26 -0
- adijif/d2/d2lib.h +81 -0
- adijif/draw.py +498 -0
- adijif/fpgas/__init__.py +1 -0
- adijif/fpgas/fpga.py +64 -0
- adijif/fpgas/xilinx/__init__.py +1143 -0
- adijif/fpgas/xilinx/bf.py +101 -0
- adijif/fpgas/xilinx/pll.py +232 -0
- adijif/fpgas/xilinx/sevenseries.py +531 -0
- adijif/fpgas/xilinx/ultrascaleplus.py +485 -0
- adijif/fpgas/xilinx/xilinx_draw.py +516 -0
- adijif/gekko_trans.py +295 -0
- adijif/jesd.py +760 -0
- adijif/plls/__init__.py +3 -0
- adijif/plls/adf4030.py +259 -0
- adijif/plls/adf4371.py +419 -0
- adijif/plls/adf4382.py +581 -0
- adijif/plls/pll.py +103 -0
- adijif/solvers.py +54 -0
- adijif/sys/__init__.py +1 -0
- adijif/sys/s_plls.py +185 -0
- adijif/system.py +567 -0
- adijif/system_draw.py +65 -0
- adijif/types.py +151 -0
- adijif/utils.py +191 -0
- pyadi_jif-0.1.0.dist-info/METADATA +62 -0
- pyadi_jif-0.1.0.dist-info/RECORD +73 -0
- pyadi_jif-0.1.0.dist-info/WHEEL +6 -0
- pyadi_jif-0.1.0.dist-info/licenses/AUTHORS.rst +13 -0
- pyadi_jif-0.1.0.dist-info/licenses/LICENSE +277 -0
- pyadi_jif-0.1.0.dist-info/top_level.txt +1 -0
adijif/d2/__init__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# flake8: noqa
|
|
2
|
+
"""Bindings for the D2 Compiler"""
|
|
3
|
+
|
|
4
|
+
import ctypes
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
loc = os.path.dirname(os.path.abspath(__file__))
|
|
8
|
+
lib_path = os.path.join(loc, "d2lib.so")
|
|
9
|
+
|
|
10
|
+
if not os.path.exists(lib_path):
|
|
11
|
+
raise FileNotFoundError(f"Could not find {lib_path}")
|
|
12
|
+
|
|
13
|
+
library = ctypes.cdll.LoadLibrary(lib_path)
|
|
14
|
+
|
|
15
|
+
runme = library.runme
|
|
16
|
+
runme.argtypes = [ctypes.c_char_p]
|
|
17
|
+
runme.restype = ctypes.c_char_p
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def compile(code: str) -> str:
|
|
21
|
+
try:
|
|
22
|
+
graph_bytes = runme(code.encode("utf-8"))
|
|
23
|
+
return graph_bytes.decode("utf-8")
|
|
24
|
+
except Exception as e:
|
|
25
|
+
print(e)
|
|
26
|
+
return None
|
adijif/d2/d2lib.h
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/* Code generated by cmd/cgo; DO NOT EDIT. */
|
|
2
|
+
|
|
3
|
+
/* package command-line-arguments */
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
#line 1 "cgo-builtin-export-prolog"
|
|
7
|
+
|
|
8
|
+
#include <stddef.h>
|
|
9
|
+
|
|
10
|
+
#ifndef GO_CGO_EXPORT_PROLOGUE_H
|
|
11
|
+
#define GO_CGO_EXPORT_PROLOGUE_H
|
|
12
|
+
|
|
13
|
+
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
|
14
|
+
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
/* Start of preamble from import "C" comments. */
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
/* End of preamble from import "C" comments. */
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
/* Start of boilerplate cgo prologue. */
|
|
28
|
+
#line 1 "cgo-gcc-export-header-prolog"
|
|
29
|
+
|
|
30
|
+
#ifndef GO_CGO_PROLOGUE_H
|
|
31
|
+
#define GO_CGO_PROLOGUE_H
|
|
32
|
+
|
|
33
|
+
typedef signed char GoInt8;
|
|
34
|
+
typedef unsigned char GoUint8;
|
|
35
|
+
typedef short GoInt16;
|
|
36
|
+
typedef unsigned short GoUint16;
|
|
37
|
+
typedef int GoInt32;
|
|
38
|
+
typedef unsigned int GoUint32;
|
|
39
|
+
typedef long long GoInt64;
|
|
40
|
+
typedef unsigned long long GoUint64;
|
|
41
|
+
typedef GoInt64 GoInt;
|
|
42
|
+
typedef GoUint64 GoUint;
|
|
43
|
+
typedef size_t GoUintptr;
|
|
44
|
+
typedef float GoFloat32;
|
|
45
|
+
typedef double GoFloat64;
|
|
46
|
+
#ifdef _MSC_VER
|
|
47
|
+
#include <complex.h>
|
|
48
|
+
typedef _Fcomplex GoComplex64;
|
|
49
|
+
typedef _Dcomplex GoComplex128;
|
|
50
|
+
#else
|
|
51
|
+
typedef float _Complex GoComplex64;
|
|
52
|
+
typedef double _Complex GoComplex128;
|
|
53
|
+
#endif
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
static assertion to make sure the file is being used on architecture
|
|
57
|
+
at least with matching size of GoInt.
|
|
58
|
+
*/
|
|
59
|
+
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
|
|
60
|
+
|
|
61
|
+
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
|
62
|
+
typedef _GoString_ GoString;
|
|
63
|
+
#endif
|
|
64
|
+
typedef void *GoMap;
|
|
65
|
+
typedef void *GoChan;
|
|
66
|
+
typedef struct { void *t; void *v; } GoInterface;
|
|
67
|
+
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
|
|
68
|
+
|
|
69
|
+
#endif
|
|
70
|
+
|
|
71
|
+
/* End of boilerplate cgo prologue. */
|
|
72
|
+
|
|
73
|
+
#ifdef __cplusplus
|
|
74
|
+
extern "C" {
|
|
75
|
+
#endif
|
|
76
|
+
|
|
77
|
+
extern char* runme(char* namePtr);
|
|
78
|
+
|
|
79
|
+
#ifdef __cplusplus
|
|
80
|
+
}
|
|
81
|
+
#endif
|
adijif/draw.py
ADDED
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
"""Diagraming functions for different components."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from typing import Union
|
|
7
|
+
|
|
8
|
+
connection_style_types = {
|
|
9
|
+
"data": {"stroke": "blue", "stroke-width": 2},
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Node:
|
|
14
|
+
"""Node model for diagraming which can have children and connections."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, name: str, ntype: str = None, parent: Node = None) -> None:
|
|
17
|
+
"""Initialize node with name, type and parent node.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
name (str): Name of the node.
|
|
21
|
+
ntype (str): Type of the node.
|
|
22
|
+
parent (Node): Parent node of the node.
|
|
23
|
+
"""
|
|
24
|
+
self.name = name
|
|
25
|
+
self.parent = parent
|
|
26
|
+
self.ntype = ntype
|
|
27
|
+
self.children = []
|
|
28
|
+
self.connections = []
|
|
29
|
+
self.shape = "rectangle"
|
|
30
|
+
self.use_unit_conversion_for_rate = True
|
|
31
|
+
self._value = None
|
|
32
|
+
|
|
33
|
+
def __repr__(self) -> str:
|
|
34
|
+
"""Get string representation of the node.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
str: String representation of the node.
|
|
38
|
+
"""
|
|
39
|
+
return f"Node({self.name})"
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def value(self) -> str:
|
|
43
|
+
"""Get value of the node.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
str: Value of the node.
|
|
47
|
+
"""
|
|
48
|
+
return f"{self.name} = {self._value}" if self._value else None
|
|
49
|
+
|
|
50
|
+
@value.setter
|
|
51
|
+
def value(self, value: Union(int, float, str)) -> None:
|
|
52
|
+
"""Set value of the node."""
|
|
53
|
+
self._value = value
|
|
54
|
+
|
|
55
|
+
def add_connection(self, connection: dict) -> None:
|
|
56
|
+
"""Add connection between this node and another node.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
connection (dict): Connection dictionary with keys "from", "to",
|
|
60
|
+
optionally "rate" and "style".
|
|
61
|
+
"""
|
|
62
|
+
if "rate" in connection and self.use_unit_conversion_for_rate:
|
|
63
|
+
units = ["Hz", "kHz", "MHz", "GHz"]
|
|
64
|
+
rate = connection["rate"]
|
|
65
|
+
# Convert to unit based on rate scale
|
|
66
|
+
for unit in units:
|
|
67
|
+
if rate < 1000:
|
|
68
|
+
connection["rate"] = f"{rate:.2f} {unit}"
|
|
69
|
+
break
|
|
70
|
+
rate /= 1000
|
|
71
|
+
|
|
72
|
+
self.connections.append(connection)
|
|
73
|
+
|
|
74
|
+
def get_connection(self, from_s: str, to: str) -> dict:
|
|
75
|
+
"""Get connection between this node and another node.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
from_s (str): Name of the node from which connection originates.
|
|
79
|
+
to (str): Name of the node to which connection goes.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
dict: Connection dictionary with keys "from", "to" and optionally "rate".
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
ValueError: If connection not found.
|
|
86
|
+
"""
|
|
87
|
+
for conn in self.connections:
|
|
88
|
+
if conn["from"].name == from_s and conn["to"].name == to:
|
|
89
|
+
return conn
|
|
90
|
+
raise ValueError(f"Connection from {from_s} to {to} not found.")
|
|
91
|
+
|
|
92
|
+
def remove_connection(self, from_s: str, to: str) -> None:
|
|
93
|
+
"""Remove connection between this node and another node.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
from_s (str): Name of the node from which connection originates.
|
|
97
|
+
to (str): Name of the node to which connection goes.
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
ValueError: If connection not found.
|
|
101
|
+
"""
|
|
102
|
+
conn_to_remove = None
|
|
103
|
+
for conn in self.connections:
|
|
104
|
+
if conn["from"].name == from_s and conn["to"].name == to:
|
|
105
|
+
conn_to_remove = conn
|
|
106
|
+
break
|
|
107
|
+
if conn_to_remove:
|
|
108
|
+
self.connections.remove(conn_to_remove)
|
|
109
|
+
return
|
|
110
|
+
raise ValueError(f"Connection from {from_s} to {to} not found.")
|
|
111
|
+
|
|
112
|
+
def update_connection(self, from_s: str, to: str, rate: Union(int, float)) -> None:
|
|
113
|
+
"""Update connection rate between this node and another node.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
from_s (str): Name of the node from which connection originates.
|
|
117
|
+
to (str): Name of the node to which connection goes.
|
|
118
|
+
rate (float): Rate of the connection.
|
|
119
|
+
|
|
120
|
+
Raises:
|
|
121
|
+
ValueError: If connection not found.
|
|
122
|
+
"""
|
|
123
|
+
for conn in self.connections:
|
|
124
|
+
if conn["from"].name == from_s and conn["to"].name == to:
|
|
125
|
+
units = ["Hz", "kHz", "MHz", "GHz"]
|
|
126
|
+
# Convert to unit based on rate scale
|
|
127
|
+
for unit in units:
|
|
128
|
+
if rate < 1000:
|
|
129
|
+
conn["rate"] = f"{rate:.2f} {unit}"
|
|
130
|
+
return
|
|
131
|
+
rate /= 1000
|
|
132
|
+
|
|
133
|
+
raise ValueError(f"Connection from {from_s} to {to} not found.")
|
|
134
|
+
|
|
135
|
+
def add_child(self, child: Node) -> None:
|
|
136
|
+
"""Add child node to this node.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
child (Node): Child node to add.
|
|
140
|
+
"""
|
|
141
|
+
if not isinstance(child, list):
|
|
142
|
+
child = [child]
|
|
143
|
+
for c in child:
|
|
144
|
+
c.parent = self
|
|
145
|
+
self.children.append(c)
|
|
146
|
+
|
|
147
|
+
def get_child(self, name: str) -> Node:
|
|
148
|
+
"""Get child node by name.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
name (str): Name of the child node.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Node: Child node with the given name.
|
|
155
|
+
|
|
156
|
+
Raises:
|
|
157
|
+
ValueError: If child node not found.
|
|
158
|
+
"""
|
|
159
|
+
for child in self.children:
|
|
160
|
+
if child.name == name:
|
|
161
|
+
return child
|
|
162
|
+
raise ValueError(f"Child with name {name} not found.")
|
|
163
|
+
|
|
164
|
+
def remove_child(self, name: str) -> None:
|
|
165
|
+
"""Remove child node by name.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
name (str): Name of the child node to remove.
|
|
169
|
+
"""
|
|
170
|
+
# Remove connections with the child first
|
|
171
|
+
connections_to_keep = []
|
|
172
|
+
for conn in self.connections:
|
|
173
|
+
if conn["to"].name != name:
|
|
174
|
+
connections_to_keep.append(conn)
|
|
175
|
+
self.connections = connections_to_keep
|
|
176
|
+
connections_to_keep = []
|
|
177
|
+
for conn in self.connections:
|
|
178
|
+
if conn["from"].name != name:
|
|
179
|
+
connections_to_keep.append(conn)
|
|
180
|
+
self.connections = connections_to_keep
|
|
181
|
+
|
|
182
|
+
children_to_keep = []
|
|
183
|
+
for child in self.children:
|
|
184
|
+
if child.name != name:
|
|
185
|
+
children_to_keep.append(child)
|
|
186
|
+
self.children = children_to_keep
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class Layout:
|
|
190
|
+
"""Layout model for diagraming which contains nodes and connections."""
|
|
191
|
+
|
|
192
|
+
si = " "
|
|
193
|
+
use_d2_cli = False
|
|
194
|
+
_write_out_d2_file = True
|
|
195
|
+
|
|
196
|
+
def __init__(self, name: str) -> None:
|
|
197
|
+
"""Initialize layout with name.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
name (str): Name of the layout.
|
|
201
|
+
"""
|
|
202
|
+
self.name = name
|
|
203
|
+
self.nodes = []
|
|
204
|
+
self.connections = []
|
|
205
|
+
self.use_unit_conversion_for_rate = True
|
|
206
|
+
self.output_filename = "clocks.d2"
|
|
207
|
+
self.output_image_filename = "clocks.svg"
|
|
208
|
+
self.layout_engine = "elk"
|
|
209
|
+
self.show_rates = True
|
|
210
|
+
|
|
211
|
+
def add_node(self, node: Node) -> None:
|
|
212
|
+
"""Add node to the layout.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
node (Node): Node to add to the layout.
|
|
216
|
+
"""
|
|
217
|
+
self.nodes.append(node)
|
|
218
|
+
|
|
219
|
+
def remove_node(self, name: str) -> None:
|
|
220
|
+
"""Remove node by name.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
name (str): Name of the node to remove.
|
|
224
|
+
"""
|
|
225
|
+
# Remove connections with the node first
|
|
226
|
+
connections_to_keep = []
|
|
227
|
+
for conn in self.connections:
|
|
228
|
+
if conn["to"].name != name:
|
|
229
|
+
connections_to_keep.append(conn)
|
|
230
|
+
self.connections = connections_to_keep
|
|
231
|
+
connections_to_keep = []
|
|
232
|
+
for conn in self.connections:
|
|
233
|
+
if conn["from"].name != name:
|
|
234
|
+
connections_to_keep.append(conn)
|
|
235
|
+
self.connections = connections_to_keep
|
|
236
|
+
|
|
237
|
+
nodes_to_keep = []
|
|
238
|
+
for node in self.nodes:
|
|
239
|
+
if node.name != name:
|
|
240
|
+
nodes_to_keep.append(node)
|
|
241
|
+
self.nodes = nodes_to_keep
|
|
242
|
+
|
|
243
|
+
def add_connection(self, connection: dict) -> None:
|
|
244
|
+
"""Add connection between two nodes.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
connection (dict): Connection dictionary with keys "from", "to"
|
|
248
|
+
and optionally "rate".
|
|
249
|
+
"""
|
|
250
|
+
if "rate" in connection and self.use_unit_conversion_for_rate:
|
|
251
|
+
units = ["Hz", "kHz", "MHz", "GHz"]
|
|
252
|
+
rate = connection["rate"]
|
|
253
|
+
# Convert to unit based on rate scale
|
|
254
|
+
for unit in units:
|
|
255
|
+
if rate < 1000:
|
|
256
|
+
connection["rate"] = f"{rate:.2f} {unit}"
|
|
257
|
+
break
|
|
258
|
+
rate /= 1000
|
|
259
|
+
self.connections.append(connection)
|
|
260
|
+
|
|
261
|
+
def get_connection(
|
|
262
|
+
self, from_s: str = None, to: str = None
|
|
263
|
+
) -> Union[dict, list[dict]]:
|
|
264
|
+
"""Get connection between two nodes.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
from_s (str): Name of the node from which connection originates.
|
|
268
|
+
to (str): Name of the node to which connection goes.
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
List[dict] or dict: List of or single Connection dictionary with
|
|
272
|
+
keys "from", "to" and optionally "rate".
|
|
273
|
+
|
|
274
|
+
Raises:
|
|
275
|
+
ValueError: If connection not found.
|
|
276
|
+
"""
|
|
277
|
+
if from_s is None and to is None:
|
|
278
|
+
raise ValueError("Both from and to cannot be None.")
|
|
279
|
+
found = []
|
|
280
|
+
if from_s is None:
|
|
281
|
+
for conn in self.connections:
|
|
282
|
+
if conn["to"].name == to:
|
|
283
|
+
found.append(conn)
|
|
284
|
+
return found
|
|
285
|
+
if to is None:
|
|
286
|
+
for conn in self.connections:
|
|
287
|
+
if conn["from"].name == from_s:
|
|
288
|
+
found.append(conn)
|
|
289
|
+
return found
|
|
290
|
+
for conn in self.connections:
|
|
291
|
+
if conn["from"].name == from_s and conn["to"].name == to:
|
|
292
|
+
return conn
|
|
293
|
+
|
|
294
|
+
def get_node(self, name: str) -> Node:
|
|
295
|
+
"""Get node by name.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
name (str): Name of the node.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
Node: Node with the given name.
|
|
302
|
+
|
|
303
|
+
Raises:
|
|
304
|
+
ValueError: If node not found.
|
|
305
|
+
"""
|
|
306
|
+
for node in self.nodes:
|
|
307
|
+
if node.name == name:
|
|
308
|
+
return node
|
|
309
|
+
raise ValueError(f"Node with name {name} not found.")
|
|
310
|
+
|
|
311
|
+
def draw(self) -> str:
|
|
312
|
+
"""Draw diagram in d2 language.
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
str: Path to the output image file.
|
|
316
|
+
"""
|
|
317
|
+
diag = "direction: right\n\n"
|
|
318
|
+
|
|
319
|
+
def apply_connection_style(diag: str, connection: dict) -> str:
|
|
320
|
+
"""Apply style to the connection in the diagram.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
diag (str): Current diagram string.
|
|
324
|
+
connection (dict): Connection dictionary with keys "from", "to",
|
|
325
|
+
and optionally "style".
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
str: Updated diagram string with applied style.
|
|
329
|
+
"""
|
|
330
|
+
from_p_name = get_parents_names(connection["from"])
|
|
331
|
+
to_p_name = get_parents_names(connection["to"])
|
|
332
|
+
|
|
333
|
+
if "type" in connection and connection["type"] in connection_style_types:
|
|
334
|
+
style = connection_style_types[connection["type"]]
|
|
335
|
+
index = 0 # Default index but make sure its not in the diag
|
|
336
|
+
for key in style:
|
|
337
|
+
while True:
|
|
338
|
+
con_str = f"({from_p_name}{connection['from'].name} -> "
|
|
339
|
+
con_str += f"{to_p_name}{connection['to'].name})[{index}]"
|
|
340
|
+
con_str += f".style.{key}: {style[key]}\n"
|
|
341
|
+
if con_str not in diag:
|
|
342
|
+
diag += con_str
|
|
343
|
+
break
|
|
344
|
+
index += 1 # Increment index to avoid duplicates
|
|
345
|
+
# Duplicates can happen if multiple connections
|
|
346
|
+
# between the same nodes
|
|
347
|
+
|
|
348
|
+
index = 0
|
|
349
|
+
if "index" in connection:
|
|
350
|
+
index = connection["index"]
|
|
351
|
+
if "style" in connection:
|
|
352
|
+
for key in connection["style"]:
|
|
353
|
+
diag += f"({from_p_name}{connection['from'].name} -> "
|
|
354
|
+
diag += f"{to_p_name}{connection['to'].name})[{index}]"
|
|
355
|
+
diag += f".style.{key}: {connection['style'][key]}\n"
|
|
356
|
+
return diag
|
|
357
|
+
|
|
358
|
+
def get_parents_names(node: Node) -> str:
|
|
359
|
+
"""Get names of all parent nodes of the given node.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
node (Node): Node for which to get parent names.
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
str: Names of all parent nodes of the given node.
|
|
366
|
+
"""
|
|
367
|
+
parents = []
|
|
368
|
+
while node.parent:
|
|
369
|
+
parents.append(node.parent.name)
|
|
370
|
+
node = node.parent
|
|
371
|
+
if not parents:
|
|
372
|
+
return ""
|
|
373
|
+
return ".".join(parents[::-1]) + "."
|
|
374
|
+
|
|
375
|
+
def draw_subnodes(node: Node, spacing: str = " ") -> str:
|
|
376
|
+
"""Draw subnodes of the given node.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
node (Node): Node for which to draw subnodes.
|
|
380
|
+
spacing (str): Spacing for indentation.
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
str: Subnodes of the given node.
|
|
384
|
+
"""
|
|
385
|
+
diag = " {\n"
|
|
386
|
+
for child in node.children:
|
|
387
|
+
if child.value:
|
|
388
|
+
diag += spacing + child.name + f": {{tooltip: {child.value} }}"
|
|
389
|
+
else:
|
|
390
|
+
diag += spacing + child.name
|
|
391
|
+
if child.children:
|
|
392
|
+
diag += draw_subnodes(child, spacing + " ")
|
|
393
|
+
else:
|
|
394
|
+
diag += "\n"
|
|
395
|
+
diag += spacing + child.name + ".shape: " + child.shape + "\n"
|
|
396
|
+
lr = len(" ")
|
|
397
|
+
diag += spacing[:-lr] + "}\n"
|
|
398
|
+
return diag
|
|
399
|
+
|
|
400
|
+
# Add all nodes
|
|
401
|
+
for node in self.nodes:
|
|
402
|
+
diag += f"{node.name}"
|
|
403
|
+
if node.children:
|
|
404
|
+
diag += draw_subnodes(node)
|
|
405
|
+
diag += "\n"
|
|
406
|
+
|
|
407
|
+
diag += "\n"
|
|
408
|
+
|
|
409
|
+
# # Set shapes
|
|
410
|
+
# for node in self.nodes:
|
|
411
|
+
# parents_string = get_parents_names(node)
|
|
412
|
+
# diag += f"{parents_string}{node.name}.shape = {node.shape}\n"
|
|
413
|
+
|
|
414
|
+
# diag += "\n"
|
|
415
|
+
|
|
416
|
+
# Add all connections
|
|
417
|
+
for connection in self.connections:
|
|
418
|
+
from_p_name = get_parents_names(connection["from"])
|
|
419
|
+
to_p_name = get_parents_names(connection["to"])
|
|
420
|
+
if self.show_rates:
|
|
421
|
+
label = f"{connection['rate']}" if "rate" in connection else None
|
|
422
|
+
else:
|
|
423
|
+
label = None
|
|
424
|
+
diag += f"{from_p_name}{connection['from'].name} ->"
|
|
425
|
+
diag += f" {to_p_name}{connection['to'].name}"
|
|
426
|
+
diag += ": " + label if label else ""
|
|
427
|
+
diag += "\n"
|
|
428
|
+
diag = apply_connection_style(diag, connection)
|
|
429
|
+
|
|
430
|
+
def draw_nodes_connections(nodes: list[Node], show_rates: bool) -> str:
|
|
431
|
+
"""Draw connections between nodes.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
nodes (list[Node]): List of nodes to draw connections between.
|
|
435
|
+
show_rates (bool): Whether to show rates in the diagram.
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
str: Connections between nodes.
|
|
439
|
+
"""
|
|
440
|
+
diag = ""
|
|
441
|
+
for node in nodes:
|
|
442
|
+
for connection in node.connections:
|
|
443
|
+
from_p_name = get_parents_names(connection["from"])
|
|
444
|
+
to_p_name = get_parents_names(connection["to"])
|
|
445
|
+
if show_rates:
|
|
446
|
+
label = f"{connection['rate']}" if "rate" in connection else ""
|
|
447
|
+
else:
|
|
448
|
+
label = ""
|
|
449
|
+
diag += f"{from_p_name}{connection['from'].name} -> "
|
|
450
|
+
diag += f"{to_p_name}{connection['to'].name}"
|
|
451
|
+
diag += ": " + label if label else ""
|
|
452
|
+
diag += "\n"
|
|
453
|
+
diag = apply_connection_style(diag, connection)
|
|
454
|
+
|
|
455
|
+
if node.children:
|
|
456
|
+
diag += draw_nodes_connections(node.children, show_rates)
|
|
457
|
+
|
|
458
|
+
return diag
|
|
459
|
+
|
|
460
|
+
# for node in self.nodes:
|
|
461
|
+
diag += draw_nodes_connections(self.nodes, show_rates=self.show_rates)
|
|
462
|
+
|
|
463
|
+
# for node in self.nodes:
|
|
464
|
+
# for connection in node.connections:
|
|
465
|
+
# from_p_name = get_parents_names(connection["from"])
|
|
466
|
+
# to_p_name = get_parents_names(connection["to"])
|
|
467
|
+
# label = f"{connection['rate']}" if "rate" in connection else ""
|
|
468
|
+
# diag += f"{from_p_name}{connection['from'].name} -> "
|
|
469
|
+
# diag += f"{to_p_name}{connection['to'].name}"
|
|
470
|
+
# diag += ": " + label if label else ""
|
|
471
|
+
# diag += "\n"
|
|
472
|
+
|
|
473
|
+
if self.use_d2_cli:
|
|
474
|
+
with open(self.output_filename, "w") as f:
|
|
475
|
+
f.write(diag)
|
|
476
|
+
|
|
477
|
+
cmd = (
|
|
478
|
+
f"d2 --theme=0 --dark-theme=200 -l {self.layout_engine} "
|
|
479
|
+
+ f"{self.output_filename} "
|
|
480
|
+
)
|
|
481
|
+
cmd += f"{self.output_image_filename}"
|
|
482
|
+
os.system(cmd) # noqa: S605
|
|
483
|
+
return self.output_image_filename
|
|
484
|
+
else:
|
|
485
|
+
if self._write_out_d2_file:
|
|
486
|
+
with open(self.output_filename, "w") as f:
|
|
487
|
+
f.write(diag)
|
|
488
|
+
print(f"Saved to {self.output_filename}")
|
|
489
|
+
|
|
490
|
+
# Use bindings
|
|
491
|
+
from .d2 import compile
|
|
492
|
+
|
|
493
|
+
out = compile(diag)
|
|
494
|
+
# with open(self.output_image_filename, "w") as f:
|
|
495
|
+
# f.write(out)
|
|
496
|
+
|
|
497
|
+
# return self.output_image_filename
|
|
498
|
+
return out
|
adijif/fpgas/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""ADI JIF FPGA Models and Utilities."""
|
adijif/fpgas/fpga.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""FPGA parent metaclass to maintain consistency for all FPGA classes."""
|
|
2
|
+
|
|
3
|
+
from abc import ABCMeta, abstractmethod
|
|
4
|
+
from typing import Dict, List, Optional, Union
|
|
5
|
+
|
|
6
|
+
from adijif.common import core
|
|
7
|
+
from adijif.converters.converter import converter as conv
|
|
8
|
+
from adijif.gekko_trans import gekko_translation
|
|
9
|
+
from adijif.solvers import CpoSolveResult
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class fpga(core, gekko_translation, metaclass=ABCMeta):
|
|
13
|
+
"""Parent metaclass for all FPGA classes."""
|
|
14
|
+
|
|
15
|
+
"""Generate another clock for link layer output.
|
|
16
|
+
This is used when the GEARBOX is enabled in HDL within link layer core.
|
|
17
|
+
"""
|
|
18
|
+
requires_separate_link_layer_out_clock: bool = False
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def determine_cpll(self) -> None:
|
|
23
|
+
"""Try to use CPLL for clocking.
|
|
24
|
+
|
|
25
|
+
This method is only used in brute-force classes
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
NotImplementedError: Method not implemented
|
|
29
|
+
"""
|
|
30
|
+
raise NotImplementedError # pragma: no cover
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def determine_qpll(self) -> None:
|
|
35
|
+
"""Try to use QPLL for clocking.
|
|
36
|
+
|
|
37
|
+
This method is only used in brute-force classes
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
NotImplementedError: Method not implemented
|
|
41
|
+
"""
|
|
42
|
+
raise NotImplementedError # pragma: no cover
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def get_config( # type: ignore
|
|
47
|
+
self,
|
|
48
|
+
converter: conv,
|
|
49
|
+
fpga_ref: Union[float, int],
|
|
50
|
+
solution: Optional[CpoSolveResult] = None,
|
|
51
|
+
) -> Union[List[Dict], Dict]:
|
|
52
|
+
"""Extract clocking configuration from solutions.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
converter (conv): Converter object connected to FPGA who config is
|
|
56
|
+
collected
|
|
57
|
+
fpga_ref (int or float): Reference clock generated for FPGA for specific
|
|
58
|
+
converter
|
|
59
|
+
solution (CpoSolveResult): CPlex solution. Only needed for CPlex solver
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
NotImplementedError: Method not implemented
|
|
63
|
+
"""
|
|
64
|
+
raise NotImplementedError # pragma: no cover
|