Skip to content

Commit 83c81e0

Browse files
authored
Merge pull request #20 from liambeguin/lvb/adrv9009-parser
adrv9009 parser
2 parents 4429447 + b6f4c5b commit 83c81e0

20 files changed

+51963
-3
lines changed

adidt/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from adidt.parts.hmc7044 import hmc7044_dt
44
from adidt.parts.ad9523_1 import ad9523_1_dt
55
from adidt.parts.ad9545 import ad9545_dt
6+
from adidt.parts.adrv9009 import adrv9009_dt
67

78
from adidt.boards.daq2 import daq2
89
from adidt.boards.ad9081_fmc import ad9081_fmc
10+
from adidt.boards.adrv9009_zu11eg import adrv9009_zu11eg
11+
from adidt.boards.adrv9009_pcbz import adrv9009_pcbz

adidt/boards/adrv9009_pcbz.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
from .layout import layout
2+
from ..parts import ad9528, adrv9009
3+
import numpy as np
4+
from pathlib import Path
5+
import logging
6+
7+
8+
class adrv9009_pcbz(layout):
9+
"""ADRV9009-PCBZ FMC board layout map for clocks and DSP"""
10+
11+
template_filename = "adi-adrv9009.dtsi"
12+
output_filename = "adi-adrv9009.dtsi"
13+
14+
profile = None
15+
16+
def __init__(self):
17+
self.jesd204 = {
18+
"framerA": {
19+
"bankId": 1,
20+
"deviceId": 0,
21+
"lane0Id": 0,
22+
"M": 4,
23+
"K": 32,
24+
"F": 4,
25+
"Np": 16,
26+
"scramble": 1,
27+
"externalSysref": 1,
28+
"serializerLanesEnabled": 0x03,
29+
"serializerLaneCrossbar": 0xE4,
30+
"lmfcOffset": 31,
31+
"newSysrefOnRelink": 0,
32+
"syncbInSelect": 0,
33+
"overSample": 0,
34+
"syncbInLvdsMode": 1,
35+
"syncbInLvdsPnInvert": 0,
36+
"enableManualLaneXbar": 0,
37+
},
38+
"framerB": {
39+
"bankId": 0,
40+
"deviceId": 0,
41+
"lane0Id": 0,
42+
"M": 4,
43+
"K": 32,
44+
"F": 4,
45+
"Np": 16,
46+
"scramble": 1,
47+
"externalSysref": 1,
48+
"serializerLanesEnabled": 0x0C,
49+
"serializerLaneCrossbar": 0xE4,
50+
"lmfcOffset": 31,
51+
"newSysrefOnRelink": 0,
52+
"syncbInSelect": 1,
53+
"overSample": 0,
54+
"syncbInLvdsMode": 1,
55+
"syncbInLvdsPnInvert": 0,
56+
"enableManualLaneXbar": 0,
57+
},
58+
"deframerA": {
59+
"bankId": 0,
60+
"deviceId": 0,
61+
"lane0Id": 0,
62+
"M": 4,
63+
"K": 32,
64+
"scramble": 1,
65+
"externalSysref": 1,
66+
"deserializerLanesEnabled": 0x0F,
67+
"deserializerLaneCrossbar": 0xE4,
68+
"lmfcOffset": 17,
69+
"newSysrefOnRelink": 0,
70+
"syncbOutSelect": 0,
71+
"Np": 16,
72+
"syncbOutLvdsMode": 1,
73+
"syncbOutLvdsPnInvert": 0,
74+
"syncbOutCmosSlewRate": 0,
75+
"syncbOutCmosDriveLevel": 0,
76+
"enableManualLaneXbar": 0,
77+
},
78+
"serAmplitude": 15,
79+
"serPreEmphasis": 1,
80+
"serInvertLanePolarity": 0,
81+
"desInvertLanePolarity": 0,
82+
"desEqSetting": 1,
83+
"sysrefLvdsMode": 1,
84+
"sysrefLvdsPnInvert": 0,
85+
}
86+
87+
def parse_profile(self, filename: Path):
88+
"""Parse a profile file.
89+
90+
Args:
91+
filename: Profile file name.
92+
93+
Returns:
94+
dict: Profile configuration.
95+
"""
96+
if not filename.exists():
97+
raise Exception(f"Profile file not found: {filename}")
98+
99+
self.xcvr_profile = adrv9009.parse_profile(filename)
100+
101+
ad9528_file = filename.parent / (filename.stem + '_AD9528.txt')
102+
if not ad9528_file.exists():
103+
raise Exception(f"AD9528 Profile file not found: {ad9528_file}")
104+
105+
self.clock_profile = ad9528.parse_profile(ad9528_file)
106+
107+
def parse_talInit(self, filename: Path):
108+
if filename is None:
109+
logging.warning("No talise_config.c passed, using defaults")
110+
return
111+
112+
logging.info(f"loading JESD204 parameters from {filename}")
113+
talInit = adrv9009.parse_talInit(filename)
114+
self.jesd204 = talInit['jesd204Settings']
115+
116+
def gen_dt_preprocess(self):
117+
return {
118+
"pll1": self.clock_profile["pll1"],
119+
"pll2": self.clock_profile["pll2"],
120+
"sysref": self.clock_profile["sysref"],
121+
"out": self.clock_profile["out"],
122+
123+
"rx": self.xcvr_profile["rx"],
124+
"tx": self.xcvr_profile["tx"],
125+
"orx": self.xcvr_profile["orx"],
126+
"lpbk": self.xcvr_profile["lpbk"],
127+
"clocks": self.xcvr_profile["clocks"],
128+
"jesd204": self.jesd204,
129+
}

adidt/boards/adrv9009_zu11eg.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
from .layout import layout
2+
from ..parts.adrv9009 import parse_profile
3+
import numpy as np
4+
import os
5+
6+
7+
class adrv9009_zu11eg(layout):
8+
"""ADRV9009-ZU11EG SOM board layout map for clocks and DSP"""
9+
10+
clock = "HMC7044"
11+
12+
adc = "adrv9009_rx"
13+
dac = "adrv9009_tx"
14+
15+
template_filename = "adrv9009_zu11eg.dts"
16+
output_filename = "adrv9009_zu11eg_out.dts"
17+
18+
profile = None
19+
20+
def make_ints(self, cfg, keys):
21+
"""Convert keys in a dict to integers.
22+
23+
Args:
24+
cfg (dict): Configuration.
25+
keys (list): Keys to convert.
26+
27+
Returns:
28+
dict: Configuration with keys converted to integers.
29+
"""
30+
for key in keys:
31+
if isinstance(cfg[key], float) and cfg[key].is_integer():
32+
cfg[key] = int(cfg[key])
33+
return cfg
34+
35+
def map_jesd_structs(self, cfg):
36+
"""Map JIF configuration to integer structs.
37+
38+
Args:
39+
cfg (dict): JIF configuration.
40+
41+
Returns:
42+
dict: ADC JESD structs.
43+
dict: DAC JESD structs.
44+
"""
45+
adc = cfg["converter"]
46+
adc["jesd"] = cfg["jesd_adc"]
47+
adc["jesd"]["jesd_class_int"] = self.map_jesd_subclass(
48+
adc["jesd"]["jesd_class"]
49+
)
50+
dac = cfg["converter"].copy()
51+
dac["jesd"] = cfg["jesd_dac"]
52+
dac["jesd"]["jesd_class_int"] = self.map_jesd_subclass(
53+
dac["jesd"]["jesd_class"]
54+
)
55+
56+
adc["jesd"] = self.make_ints(adc["jesd"], ["converter_clock", "sample_clock"])
57+
dac["jesd"] = self.make_ints(dac["jesd"], ["converter_clock", "sample_clock"])
58+
59+
adc["datapath"] = cfg["datapath_adc"]
60+
dac["datapath"] = cfg["datapath_dac"]
61+
62+
return adc, dac
63+
64+
def map_clocks_to_board_layout(self, cfg):
65+
"""Map JIF configuration to board clock connection layout.
66+
67+
Args:
68+
cfg (dict): JIF configuration.
69+
70+
Returns:
71+
dict: Board clock connection layout.
72+
"""
73+
# Fix ups
74+
for key in ["vco", "vcxo"]:
75+
if isinstance(cfg["clock"][key], float) and cfg["clock"][key].is_integer():
76+
cfg["clock"][key] = int(cfg["clock"][key])
77+
78+
map = {}
79+
clk = cfg["clock"]["output_clocks"]
80+
81+
# Common
82+
map["DEV_REFCLK"] = {
83+
"source_port": 2,
84+
"divider": clk["AD9081_ref_clk"]["divider"],
85+
}
86+
map["DEV_SYSREF"] = {
87+
"source_port": 3,
88+
"divider": np.max(
89+
[clk["adc_sysref"]["divider"], clk["dac_sysref"]["divider"]]
90+
),
91+
}
92+
map["FPGA_SYSREF"] = {
93+
"source_port": 13,
94+
"divider": np.max(
95+
[clk["adc_fpga_ref_clk"]["divider"], clk["dac_fpga_ref_clk"]["divider"]]
96+
),
97+
}
98+
99+
# RX side
100+
map["CORE_CLK_RX"] = {
101+
"source_port": 0,
102+
"divider": clk["adc_fpga_ref_clk"]["divider"],
103+
}
104+
map["CORE_CLK_RX_ALT"] = {
105+
"source_port": 10,
106+
"divider": clk["adc_fpga_ref_clk"]["divider"] * 2,
107+
}
108+
map["FPGA_REFCLK1"] = {
109+
"source_port": 8,
110+
"divider": clk["adc_fpga_ref_clk"]["divider"],
111+
}
112+
113+
# Tx side
114+
map["CORE_CLK_TX"] = {
115+
"source_port": 6,
116+
"divider": clk["dac_fpga_ref_clk"]["divider"],
117+
}
118+
map["FPGA_REFCLK2"] = {
119+
"source_port": 12,
120+
"divider": clk["dac_fpga_ref_clk"]["divider"],
121+
}
122+
123+
ccfg = {"map": map, "clock": cfg["clock"]}
124+
125+
fpga = {}
126+
fpga["fpga_adc"] = cfg["fpga_adc"]
127+
fpga["fpga_dac"] = cfg["fpga_dac"]
128+
129+
# Check all clocks are mapped
130+
# FIXME
131+
132+
# Check no source_port is mapped to more than one clock
133+
# FIXME
134+
adc, dac = self.map_jesd_structs(cfg)
135+
136+
return ccfg, adc, dac, fpga
137+
138+
def parse_profile(self, filename):
139+
"""Parse a profile file.
140+
141+
Args:
142+
filename (str): Profile file name.
143+
144+
Returns:
145+
dict: Profile configuration.
146+
"""
147+
if not os.path.exists(filename):
148+
raise Exception(f"Profile file not found: {filename}")
149+
self.profile = parse_profile(filename)

adidt/boards/layout.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ class layout:
1010
template_filename = None
1111
output_filename = None
1212

13+
# def gen_dt_preprocess(self, **kwargs):
14+
# return kwargs
15+
1316
def gen_dt(self, **kwargs):
1417
"""Generate the DT file from configuration structs.
1518
@@ -34,6 +37,8 @@ def gen_dt(self, **kwargs):
3437

3538
loc = os.path.join(self.template_filename)
3639
template = env.get_template(loc)
40+
41+
kwargs = self.gen_dt_preprocess(**kwargs)
3742
output = template.render(**kwargs)
3843

3944
with open(self.output_filename, "w") as f:

adidt/cli/main.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import fdt
33
import click
44
from .helpers import list_node_props, list_node_prop, list_node_subnodes
5+
from pathlib import Path
56

67

78
@click.group()
@@ -11,6 +12,16 @@
1112
is_flag=True,
1213
help="Disable formatting",
1314
)
15+
@click.option(
16+
"--board",
17+
"-b",
18+
default="adrv9009_pcbz",
19+
help="Set board configuration",
20+
type=click.Choice(
21+
["ad9081_fmc", "adrv9009_pcbz", "adrv9009_zu11eg", "daq2"]
22+
),
23+
show_default=True,
24+
)
1425
@click.option(
1526
"--context",
1627
"-c",
@@ -58,11 +69,12 @@
5869
show_default=True,
5970
)
6071
@click.pass_context
61-
def cli(ctx, no_color, context, ip, username, password, arch, filepath):
72+
def cli(ctx, no_color, board, context, ip, username, password, arch, filepath):
6273
"""ADI device tree utility"""
6374
ctx.ensure_object(dict)
6475

6576
ctx.obj["no_color"] = no_color
77+
ctx.obj["board"] = board
6678
ctx.obj["context"] = context
6779
ctx.obj["ip"] = ip
6880
ctx.obj["username"] = username
@@ -450,3 +462,35 @@ def jif(ctx, node_type, reboot, filename):
450462
d.update_current_dt(reboot=reboot)
451463
else:
452464
raise Exception("Other node types not implemented")
465+
466+
467+
@cli.command()
468+
@click.option(
469+
"--profile",
470+
"-p",
471+
default=None,
472+
help="",
473+
type=Path,
474+
)
475+
@click.option(
476+
"--config",
477+
"-c",
478+
required=False,
479+
default=None,
480+
help="path to talise_config.c",
481+
type=Path,
482+
)
483+
@click.pass_context
484+
def profile2dt(ctx, profile, config):
485+
"""Generate devicetree from Profile Configuration Wizard files
486+
"""
487+
b = ctx.obj["board"]
488+
if b not in ["adrv9009_pcbz"]:
489+
print(f"board type {b} not supported")
490+
return
491+
492+
board = eval(f"adidt.{b}()")
493+
board.parse_profile(profile)
494+
board.parse_talInit(config)
495+
board.gen_dt()
496+
print(f'Wrote {board.output_filename}')

0 commit comments

Comments
 (0)