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
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from adijif.converters.converter import converter
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class adrv9009_bf(converter):
|
|
7
|
+
"""Brute force methods for calculating clocks
|
|
8
|
+
|
|
9
|
+
These are currently meant for debug to compare against
|
|
10
|
+
the solver solutions
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def device_clock_available(self):
|
|
14
|
+
"""Generate list of possible device clocks"""
|
|
15
|
+
aicd = sorted(self.available_input_clock_dividers)
|
|
16
|
+
|
|
17
|
+
dev_clocks = []
|
|
18
|
+
for div in aicd:
|
|
19
|
+
in_clock = self.sample_clock * self.datapath_decimation * div
|
|
20
|
+
if in_clock <= self.max_input_clock:
|
|
21
|
+
dev_clocks.append(in_clock)
|
|
22
|
+
if not dev_clocks:
|
|
23
|
+
raise Exception(
|
|
24
|
+
"No device clocks possible in current config. Sample rate too high"
|
|
25
|
+
)
|
|
26
|
+
return dev_clocks
|
|
27
|
+
|
|
28
|
+
def device_clock_ranges(self):
|
|
29
|
+
"""Generate min and max values for device clock"""
|
|
30
|
+
|
|
31
|
+
clks = self.device_clock_available()
|
|
32
|
+
return np.min(clks), np.max(clks)
|
|
33
|
+
|
|
34
|
+
def sysref_clock_ranges(self):
|
|
35
|
+
"""sysref must be multiple of LMFC"""
|
|
36
|
+
lmfc = self.multiframe_clock
|
|
37
|
+
return lmfc / 2048, lmfc / 2
|
|
38
|
+
|
|
39
|
+
def sysref_met(self, sysref_clock, sample_clock):
|
|
40
|
+
if sysref_clock % self.multiframe_clock != 0:
|
|
41
|
+
raise Exception("SYSREF not a multiple of LMFC")
|
|
42
|
+
if (self.multiframe_clock / sysref_clock) < 2 * self.input_clock_divider:
|
|
43
|
+
raise Exception("SYSREF not a multiple of LMFC > 1")
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""ADRV9009 Utility Functions."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, Union
|
|
4
|
+
|
|
5
|
+
from adijif.converters.converter import converter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _convert_to_config(
|
|
9
|
+
L: Union[int, float],
|
|
10
|
+
M: Union[int, float],
|
|
11
|
+
Np: Union[int, float],
|
|
12
|
+
S: Union[int, float] = 1,
|
|
13
|
+
) -> Dict:
|
|
14
|
+
return {
|
|
15
|
+
"L": L,
|
|
16
|
+
"M": M,
|
|
17
|
+
"F": Np / 8 * M * S / L,
|
|
18
|
+
"S": S,
|
|
19
|
+
"HD": (
|
|
20
|
+
1
|
|
21
|
+
if (M == 1 and L == 2 and S == 1)
|
|
22
|
+
or (M == 2 and L == 4 and S == 1)
|
|
23
|
+
or (M == 1 and L == 4 and S == 2)
|
|
24
|
+
else 0
|
|
25
|
+
),
|
|
26
|
+
"Np": Np,
|
|
27
|
+
"N": Np,
|
|
28
|
+
"CS": 0,
|
|
29
|
+
"CF": 0,
|
|
30
|
+
"K": 32, # THIS IS A SUGGESTED VALUE
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
quick_configuration_modes_tx = {
|
|
35
|
+
# M = 1
|
|
36
|
+
str(0): _convert_to_config(M=1, L=1, Np=16),
|
|
37
|
+
str(1): _convert_to_config(M=1, L=2, Np=16),
|
|
38
|
+
# M = 2
|
|
39
|
+
str(2): _convert_to_config(M=2, L=1, Np=16),
|
|
40
|
+
str(3): _convert_to_config(M=2, L=2, Np=16),
|
|
41
|
+
str(4): _convert_to_config(M=2, L=4, Np=16),
|
|
42
|
+
# M = 4
|
|
43
|
+
str(5): _convert_to_config(M=4, L=1, Np=16),
|
|
44
|
+
str(6): _convert_to_config(M=4, L=2, Np=16),
|
|
45
|
+
str(7): _convert_to_config(M=4, L=4, Np=16),
|
|
46
|
+
str(8): _convert_to_config(M=4, L=2, Np=12),
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
quick_configuration_modes_rx = {
|
|
51
|
+
# M = 1
|
|
52
|
+
str(0): _convert_to_config(M=1, L=1, S=1, Np=16),
|
|
53
|
+
str(1): _convert_to_config(M=1, L=1, S=2, Np=16),
|
|
54
|
+
str(2): _convert_to_config(M=1, L=1, S=4, Np=16),
|
|
55
|
+
str(3): _convert_to_config(M=1, L=2, S=1, Np=16),
|
|
56
|
+
str(4): _convert_to_config(M=1, L=2, S=2, Np=16),
|
|
57
|
+
str(5): _convert_to_config(M=1, L=2, S=4, Np=16),
|
|
58
|
+
str(6): _convert_to_config(M=1, L=4, S=2, Np=16),
|
|
59
|
+
str(7): _convert_to_config(M=1, L=4, S=4, Np=16),
|
|
60
|
+
# M = 2
|
|
61
|
+
str(8): _convert_to_config(M=2, L=1, S=1, Np=16),
|
|
62
|
+
str(9): _convert_to_config(M=2, L=1, S=2, Np=16),
|
|
63
|
+
str(10): _convert_to_config(M=2, L=2, S=1, Np=16),
|
|
64
|
+
str(11): _convert_to_config(M=2, L=2, S=2, Np=16),
|
|
65
|
+
str(12): _convert_to_config(M=2, L=2, S=4, Np=16),
|
|
66
|
+
str(13): _convert_to_config(M=2, L=4, S=1, Np=16),
|
|
67
|
+
str(14): _convert_to_config(M=2, L=4, S=2, Np=16),
|
|
68
|
+
str(15): _convert_to_config(M=2, L=4, S=4, Np=16),
|
|
69
|
+
# M = 4
|
|
70
|
+
str(16): _convert_to_config(M=4, L=1, S=1, Np=16),
|
|
71
|
+
str(17): _convert_to_config(M=4, L=2, S=1, Np=16),
|
|
72
|
+
str(18): _convert_to_config(M=4, L=2, S=2, Np=16),
|
|
73
|
+
str(19): _convert_to_config(M=4, L=4, S=1, Np=16),
|
|
74
|
+
str(20): _convert_to_config(M=4, L=4, S=2, Np=16),
|
|
75
|
+
str(21): _convert_to_config(M=4, L=4, S=4, Np=16),
|
|
76
|
+
# Np = 12,24
|
|
77
|
+
str(22): _convert_to_config(M=2, L=1, S=2, Np=12),
|
|
78
|
+
str(23): _convert_to_config(M=4, L=1, S=1, Np=12),
|
|
79
|
+
str(24): _convert_to_config(M=4, L=2, S=1, Np=12),
|
|
80
|
+
str(25): _convert_to_config(M=2, L=2, S=2, Np=24),
|
|
81
|
+
str(26): _convert_to_config(M=4, L=2, S=1, Np=24),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _extra_jesd_check(dev: converter) -> None:
|
|
86
|
+
FK = dev.F * dev.K
|
|
87
|
+
assert FK <= 256, "F x K must be <= 256"
|
|
88
|
+
assert FK >= 20, "F x K must be >= 20"
|
|
89
|
+
assert FK % 4 == 0, "F x K must be a multiple of 4"
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
"""Converter base meta class for all converter clocking models."""
|
|
2
|
+
|
|
3
|
+
from abc import ABCMeta, abstractmethod
|
|
4
|
+
from typing import Dict, List, Union
|
|
5
|
+
|
|
6
|
+
from ..common import core
|
|
7
|
+
from ..draw import Layout, Node
|
|
8
|
+
from ..gekko_trans import gekko_translation
|
|
9
|
+
from ..jesd import jesd
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class converter(core, jesd, gekko_translation, metaclass=ABCMeta):
|
|
13
|
+
"""Converter base meta class used to enforce all converter classes.
|
|
14
|
+
|
|
15
|
+
This class should be inherited from for all converters: ADCs, DACs,
|
|
16
|
+
and transceivers.
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
"""Nested device with multiple dependent converters. Includes MxFE and
|
|
21
|
+
transceivers"""
|
|
22
|
+
_nested = False
|
|
23
|
+
|
|
24
|
+
"""DSP data path of device"""
|
|
25
|
+
datapath = None
|
|
26
|
+
|
|
27
|
+
config: Dict = {}
|
|
28
|
+
_jesd_params_to_skip = [
|
|
29
|
+
"E",
|
|
30
|
+
"global_index",
|
|
31
|
+
] # Params in table not to set
|
|
32
|
+
_jesd_params_to_skip_check = [
|
|
33
|
+
"DualLink",
|
|
34
|
+
"E",
|
|
35
|
+
"decimations",
|
|
36
|
+
"global_index",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
def draw(
|
|
40
|
+
self, clocks: Dict, lo: Layout = None, clock_chip_node: Node = None
|
|
41
|
+
) -> str:
|
|
42
|
+
"""Generic Draw converter model.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
clocks (Dict): Clocking configuration
|
|
46
|
+
lo (Layout): Layout object to draw on
|
|
47
|
+
clock_chip_node (Node): Clock chip node to add to. Defaults to None.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
str: Path to image file
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
Exception: If no solution is saved
|
|
54
|
+
"""
|
|
55
|
+
system_draw = lo is not None
|
|
56
|
+
name = self.name.lower()
|
|
57
|
+
|
|
58
|
+
if not system_draw:
|
|
59
|
+
lo = Layout(f"{name} Example")
|
|
60
|
+
else:
|
|
61
|
+
assert isinstance(lo, Layout), "lo must be a Layout object"
|
|
62
|
+
|
|
63
|
+
ic_node = Node(self.name)
|
|
64
|
+
lo.add_node(ic_node)
|
|
65
|
+
|
|
66
|
+
# rate = clocks[f"{name}_ref_clk"]
|
|
67
|
+
# Find key with ending
|
|
68
|
+
ref_clk_name = None
|
|
69
|
+
for key in clocks.keys():
|
|
70
|
+
if key.lower().endswith(f"{name.lower()}_ref_clk"):
|
|
71
|
+
ref_clk_name = key
|
|
72
|
+
break
|
|
73
|
+
if ref_clk_name is None:
|
|
74
|
+
raise Exception(
|
|
75
|
+
f"No clock found for {name}_ref_clk\n.Options: {clocks.keys()}"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
sysref_clk_name = None
|
|
79
|
+
for key in clocks.keys():
|
|
80
|
+
if key.lower().endswith(f"{name.lower()}_sysref"):
|
|
81
|
+
sysref_clk_name = key
|
|
82
|
+
break
|
|
83
|
+
if sysref_clk_name is None:
|
|
84
|
+
raise Exception(
|
|
85
|
+
f"No clock found for {name}_sysref\n.Options: {clocks.keys()}"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if not system_draw:
|
|
89
|
+
ref_in = Node("REF_IN", ntype="input")
|
|
90
|
+
lo.add_node(ref_in)
|
|
91
|
+
else:
|
|
92
|
+
to_node = lo.get_node(ref_clk_name)
|
|
93
|
+
from_node = lo.get_connection(to=to_node.name)
|
|
94
|
+
assert from_node, "No connection found"
|
|
95
|
+
assert isinstance(from_node, list), "Connection must be a list"
|
|
96
|
+
assert len(from_node) == 1, "Only one connection allowed"
|
|
97
|
+
ref_in = from_node[0]["from"]
|
|
98
|
+
# Remove to_node since it is not needed
|
|
99
|
+
lo.remove_node(to_node.name)
|
|
100
|
+
|
|
101
|
+
rate = clocks[ref_clk_name]
|
|
102
|
+
|
|
103
|
+
lo.add_connection({"from": ref_in, "to": ic_node, "rate": rate})
|
|
104
|
+
|
|
105
|
+
# Add framer
|
|
106
|
+
jesd204_framer = Node("JESD204 Framer", ntype="jesd204framer")
|
|
107
|
+
ic_node.add_child(jesd204_framer)
|
|
108
|
+
|
|
109
|
+
# SYSREF
|
|
110
|
+
if not system_draw:
|
|
111
|
+
sysref_in = Node("SYSREF_IN", ntype="input")
|
|
112
|
+
lo.add_connection(
|
|
113
|
+
{
|
|
114
|
+
"from": sysref_in,
|
|
115
|
+
"to": jesd204_framer,
|
|
116
|
+
# "rate": clocks[f"{name}_sysref"],
|
|
117
|
+
"rate": clocks[sysref_clk_name],
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
# to_node = lo.get_node(f"{name}_sysref")
|
|
122
|
+
to_node = lo.get_node(sysref_clk_name)
|
|
123
|
+
from_node = lo.get_connection(to=to_node.name)
|
|
124
|
+
assert from_node, "No connection found"
|
|
125
|
+
assert isinstance(from_node, list), "Connection must be a list"
|
|
126
|
+
assert len(from_node) == 1, "Only one connection allowed"
|
|
127
|
+
sysref_in = from_node[0]["from"]
|
|
128
|
+
lo.remove_node(to_node.name)
|
|
129
|
+
|
|
130
|
+
lo.add_connection(
|
|
131
|
+
{
|
|
132
|
+
"from": sysref_in,
|
|
133
|
+
"to": jesd204_framer,
|
|
134
|
+
# "rate": clocks[f"{name}_sysref"],
|
|
135
|
+
"rate": clocks[sysref_clk_name],
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# WIP Add remote deframer
|
|
140
|
+
jesd204_deframer = Node("JESD204 Deframer", ntype="deframer")
|
|
141
|
+
|
|
142
|
+
# Add connect for each lane
|
|
143
|
+
for _ in range(self.L):
|
|
144
|
+
lane_rate = self.bit_clock
|
|
145
|
+
lo.add_connection(
|
|
146
|
+
{"from": jesd204_framer, "to": jesd204_deframer, "rate": lane_rate}
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
if not system_draw:
|
|
150
|
+
return lo.draw()
|
|
151
|
+
|
|
152
|
+
def validate_config(self) -> None:
|
|
153
|
+
"""Validate device configuration including JESD and clocks.
|
|
154
|
+
|
|
155
|
+
This check only is for static configuration that does not include
|
|
156
|
+
variables which are solved.
|
|
157
|
+
"""
|
|
158
|
+
self._check_valid_jesd_mode()
|
|
159
|
+
self._check_valid_internal_configuration() # type: ignore
|
|
160
|
+
self.validate_clocks()
|
|
161
|
+
|
|
162
|
+
def set_quick_configuration_mode(
|
|
163
|
+
self, mode: Union[str, int], jesd_class: str = "jesd204b"
|
|
164
|
+
) -> None:
|
|
165
|
+
"""Set JESD configuration based on preset mode table.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
mode (str): Integer of desired mode.
|
|
169
|
+
jesd_class (str): JESD class to use. Default is jesd204b.
|
|
170
|
+
|
|
171
|
+
Raises:
|
|
172
|
+
Exception: Invalid mode selected
|
|
173
|
+
"""
|
|
174
|
+
smode = str(mode)
|
|
175
|
+
if smode not in self.quick_configuration_modes[jesd_class].keys():
|
|
176
|
+
raise Exception(f"Mode {smode} not among configurations for {jesd_class}")
|
|
177
|
+
|
|
178
|
+
if jesd_class not in self.available_jesd_modes:
|
|
179
|
+
raise Exception(f"{jesd_class} not available for {self.name}")
|
|
180
|
+
self.jesd_class = jesd_class
|
|
181
|
+
|
|
182
|
+
cfg = self.quick_configuration_modes[jesd_class][smode]
|
|
183
|
+
for jparam in cfg:
|
|
184
|
+
if jparam in self._jesd_params_to_skip:
|
|
185
|
+
continue
|
|
186
|
+
setattr(self, jparam, cfg[jparam])
|
|
187
|
+
|
|
188
|
+
def _check_valid_jesd_mode(self) -> str:
|
|
189
|
+
"""Verify current JESD configuration for part is valid.
|
|
190
|
+
|
|
191
|
+
Raises:
|
|
192
|
+
Exception: Invalid JESD configuration
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
str: Current JESD mode
|
|
196
|
+
"""
|
|
197
|
+
# Check to make sure JESD clocks in range
|
|
198
|
+
self._check_jesd_config()
|
|
199
|
+
# Pull current mode
|
|
200
|
+
k = next(iter(self.quick_configuration_modes[self.jesd_class]))
|
|
201
|
+
attrs = self.quick_configuration_modes[self.jesd_class][k].keys()
|
|
202
|
+
|
|
203
|
+
current_config = {}
|
|
204
|
+
for attr in attrs:
|
|
205
|
+
if attr in self._jesd_params_to_skip_check:
|
|
206
|
+
continue
|
|
207
|
+
current_config[attr] = getattr(self, attr)
|
|
208
|
+
|
|
209
|
+
# Check mode in supported modes
|
|
210
|
+
for mode in self.quick_configuration_modes[self.jesd_class].keys():
|
|
211
|
+
cmode = self.quick_configuration_modes[self.jesd_class][mode].copy()
|
|
212
|
+
for k in self._jesd_params_to_skip_check:
|
|
213
|
+
if hasattr(cmode, k) or k in cmode:
|
|
214
|
+
del cmode[k]
|
|
215
|
+
if hasattr(current_config, k) or k in current_config:
|
|
216
|
+
del current_config[k]
|
|
217
|
+
if current_config == cmode:
|
|
218
|
+
return mode
|
|
219
|
+
raise Exception(f"Invalid JESD configuration for {self.name}\n{current_config}")
|
|
220
|
+
|
|
221
|
+
def get_current_jesd_mode_settings(self) -> Dict:
|
|
222
|
+
"""Get current JESD mode settings.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Dict: Current JESD mode settings
|
|
226
|
+
str: Current JESD mode
|
|
227
|
+
"""
|
|
228
|
+
k = next(iter(self.quick_configuration_modes[self.jesd_class]))
|
|
229
|
+
attrs = self.quick_configuration_modes[self.jesd_class][k].keys()
|
|
230
|
+
current_config = {}
|
|
231
|
+
for attr in attrs:
|
|
232
|
+
if attr in self._jesd_params_to_skip_check:
|
|
233
|
+
continue
|
|
234
|
+
current_config[attr] = getattr(self, attr)
|
|
235
|
+
return current_config
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
@abstractmethod
|
|
239
|
+
def converter_type(self) -> str:
|
|
240
|
+
"""Type of converter. ADC or DAC.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
str: Type of converter
|
|
244
|
+
"""
|
|
245
|
+
raise NotImplementedError
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
@abstractmethod
|
|
249
|
+
def clocking_option_available(self) -> List[str]:
|
|
250
|
+
"""Supported clocking modes.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
List: List of string of clocking modes
|
|
254
|
+
"""
|
|
255
|
+
raise NotImplementedError # pragma: no cover
|
|
256
|
+
|
|
257
|
+
_clocking_option = "direct"
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def clocking_option(self) -> str:
|
|
261
|
+
"""Get clocking mode for device.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
str: Clocking mode for device (integrated_pll, direct)
|
|
265
|
+
"""
|
|
266
|
+
return self._clocking_option
|
|
267
|
+
|
|
268
|
+
@clocking_option.setter
|
|
269
|
+
def clocking_option(self, value: str) -> None:
|
|
270
|
+
"""Set clocking mode for device.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
value (str): Clocking mode for device (integrated_pll, direct)
|
|
274
|
+
|
|
275
|
+
Raises:
|
|
276
|
+
Exception: clocking_option not supported by device
|
|
277
|
+
"""
|
|
278
|
+
if value not in self.clocking_option_available:
|
|
279
|
+
raise Exception("clocking_option not available for device")
|
|
280
|
+
self._clocking_option = value
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
@abstractmethod
|
|
284
|
+
def quick_configuration_modes(self) -> Dict:
|
|
285
|
+
"""Supported JESD mode table.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Dict: Dictionary of supported modes
|
|
289
|
+
"""
|
|
290
|
+
raise NotImplementedError # pragma: no cover
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
@abstractmethod
|
|
294
|
+
def _check_valid_internal_configuration(self) -> None:
|
|
295
|
+
"""Check current device mode is valid."""
|
|
296
|
+
raise NotImplementedError # pragma: no cover
|
|
297
|
+
|
|
298
|
+
@property
|
|
299
|
+
@abstractmethod
|
|
300
|
+
def converter_clock_min(self) -> Union[int, float]:
|
|
301
|
+
"""Minimum rate of data converter.
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
Union[int, float]: converter min rate
|
|
305
|
+
"""
|
|
306
|
+
raise NotImplementedError # pragma: no cover
|
|
307
|
+
|
|
308
|
+
@property
|
|
309
|
+
@abstractmethod
|
|
310
|
+
def converter_clock_max(self) -> Union[int, float]:
|
|
311
|
+
"""Maximum rate of data converter.
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
Union[int, float]: converter max rate
|
|
315
|
+
"""
|
|
316
|
+
raise NotImplementedError # pragma: no cover
|
|
317
|
+
|
|
318
|
+
@property
|
|
319
|
+
@abstractmethod
|
|
320
|
+
def name(self) -> str:
|
|
321
|
+
"""Name of supported by device.
|
|
322
|
+
|
|
323
|
+
Must be a string
|
|
324
|
+
|
|
325
|
+
Raises:
|
|
326
|
+
NotImplementedError: If child classes do not implement method/property
|
|
327
|
+
"""
|
|
328
|
+
raise NotImplementedError # pragma: no cover
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
@abstractmethod
|
|
332
|
+
def device_clock_available(self) -> None:
|
|
333
|
+
"""Allowable K settings for device.
|
|
334
|
+
|
|
335
|
+
Must be a list ints
|
|
336
|
+
|
|
337
|
+
Raises:
|
|
338
|
+
NotImplementedError: If child classes do not implement method/property
|
|
339
|
+
"""
|
|
340
|
+
raise NotImplementedError # pragma: no cover
|
|
341
|
+
|
|
342
|
+
@property
|
|
343
|
+
@abstractmethod
|
|
344
|
+
def device_clock_ranges(self) -> None:
|
|
345
|
+
"""Allowable ranges for device clocks based on config.
|
|
346
|
+
|
|
347
|
+
This is only used for brute-force implementations and not solver based
|
|
348
|
+
|
|
349
|
+
Raises:
|
|
350
|
+
NotImplementedError: If child classes do not implement method/property
|
|
351
|
+
"""
|
|
352
|
+
raise NotImplementedError # pragma: no cover
|
|
353
|
+
|
|
354
|
+
@property
|
|
355
|
+
@abstractmethod
|
|
356
|
+
def get_required_clocks(self) -> List:
|
|
357
|
+
"""Get list of strings of names of requested clocks.
|
|
358
|
+
|
|
359
|
+
This list of names is for the clocks defined by get_required_clocks
|
|
360
|
+
|
|
361
|
+
Raises:
|
|
362
|
+
NotImplementedError: If child classes do not implement method/property
|
|
363
|
+
"""
|
|
364
|
+
raise NotImplementedError # pragma: no cover
|
|
365
|
+
|
|
366
|
+
@property
|
|
367
|
+
@abstractmethod
|
|
368
|
+
def get_required_clock_names(self) -> List[str]:
|
|
369
|
+
"""Generate list of required clock names.
|
|
370
|
+
|
|
371
|
+
This is a list of strings
|
|
372
|
+
|
|
373
|
+
Raises:
|
|
374
|
+
NotImplementedError: If child classes do not implement method/property
|
|
375
|
+
"""
|
|
376
|
+
raise NotImplementedError # pragma: no cover
|
|
377
|
+
|
|
378
|
+
@property
|
|
379
|
+
@abstractmethod
|
|
380
|
+
def get_config(self) -> Dict:
|
|
381
|
+
"""Extract configuration from solver solution.
|
|
382
|
+
|
|
383
|
+
Return dictionary of clocking config for converter
|
|
384
|
+
|
|
385
|
+
Raises:
|
|
386
|
+
NotImplementedError: If child classes do not implement method/property
|
|
387
|
+
"""
|
|
388
|
+
raise NotImplementedError # pragma: no cover
|
|
389
|
+
|
|
390
|
+
def _get_converters(self) -> Union["converter", List["converter"]]:
|
|
391
|
+
return self
|
|
392
|
+
|
|
393
|
+
def __str__(self) -> str:
|
|
394
|
+
"""Get string description of converter object.
|
|
395
|
+
|
|
396
|
+
Returns:
|
|
397
|
+
str: Description string
|
|
398
|
+
"""
|
|
399
|
+
return f"{self.name} data converter model"
|
adijif/converters/dac.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Converter base meta class for all converter clocking models."""
|
|
2
|
+
|
|
3
|
+
from abc import ABCMeta, abstractmethod
|
|
4
|
+
from typing import List, Union
|
|
5
|
+
|
|
6
|
+
from adijif.converters.converter import converter
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class dac(converter, metaclass=ABCMeta):
|
|
10
|
+
"""DAC base meta class used to enforce all DAC classes.
|
|
11
|
+
|
|
12
|
+
This class should be inherited from for all DACs and transceivers.
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def _check_valid_internal_configuration(self) -> None:
|
|
17
|
+
"""Verify current internal clocking configuration for part is valid.
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
Exception: Invalid clocking configuration
|
|
21
|
+
"""
|
|
22
|
+
if self.interpolation * self.sample_clock > self.converter_clock_max:
|
|
23
|
+
raise Exception(
|
|
24
|
+
"DAC rate too fast for configuration {} (max: {})".format(
|
|
25
|
+
self.interpolation * self.sample_clock,
|
|
26
|
+
self.converter_clock_max,
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
if self.interpolation * self.sample_clock < self.converter_clock_min:
|
|
30
|
+
raise Exception(
|
|
31
|
+
"DAC rate too slow for configuration {} (min: {})".format(
|
|
32
|
+
self.interpolation * self.sample_clock,
|
|
33
|
+
self.converter_clock_min,
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def interpolation_available(self) -> List:
|
|
40
|
+
"""Interpolation settings available.
|
|
41
|
+
|
|
42
|
+
Must be a list or None
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
NotImplementedError: If child classes do not implement method/property
|
|
46
|
+
"""
|
|
47
|
+
raise NotImplementedError # pragma: no cover
|
|
48
|
+
|
|
49
|
+
_interpolation = 1
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def interpolation(self) -> Union[int, float]:
|
|
53
|
+
"""Interpolation between DAC and JESD framer. If none device is not an DAC.
|
|
54
|
+
|
|
55
|
+
Generally a multiple of 2
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
int: interpolation value
|
|
59
|
+
"""
|
|
60
|
+
return self._interpolation
|
|
61
|
+
|
|
62
|
+
@interpolation.setter
|
|
63
|
+
def interpolation(self, value: int) -> None:
|
|
64
|
+
"""Interpolation between DAC and JESD framer. If none device is not an DAC.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
value (int): interpolation
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
Exception: interpolation not an integer or not in range
|
|
71
|
+
"""
|
|
72
|
+
if int(value) != value:
|
|
73
|
+
raise Exception("interpolation_available must be an integer")
|
|
74
|
+
if value not in self.interpolation_available:
|
|
75
|
+
raise Exception("interpolation_available not in range for device")
|
|
76
|
+
self._interpolation = value
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def converter_clock(self) -> Union[int, float]:
|
|
80
|
+
"""Get rate of converter in samples per second.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
float: converter clock rate in samples per second
|
|
84
|
+
"""
|
|
85
|
+
return self.sample_clock * self.interpolation
|
|
Binary file
|