diff --git a/.codespell-whitelist b/.codespell-whitelist new file mode 100644 index 00000000..0f1a1357 --- /dev/null +++ b/.codespell-whitelist @@ -0,0 +1,6 @@ +AFE +afe +SOM +som +sinc +byteorder diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c6babaa5..659b6c21 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,5 +13,6 @@ jobs: with: python-version: ${{ matrix.python-version }} architecture: x64 + - run: sudo apt install -y gcc-aarch64-linux-gnu make bc u-boot-tools flex bison libssl-dev tar kmod - run: pip install -r requirements/requirements_dev.txt - run: pytest -vs diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..6718158e --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,36 @@ +repos: + #- repo: https://github.com/codespell-project/codespell + # rev: v1.15.0 + # hooks: + # - id: codespell + # args: [--ignore-words=.codespell-whitelist,--exclude-file=examples/cn0549/ml_fan_example.ipynb] +- repo: https://github.com/pre-commit/mirrors-isort + rev: v4.3.20 + hooks: + - id: isort + #- repo: https://github.com/pre-commit/mirrors-mypy + # rev: v0.720 + # hooks: + # - id: mypy + # args: [--no-strict-optional, --ignore-missing-imports] +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - id: check-merge-conflict + - id: debug-statements + - id: check-docstring-first + - id: flake8 +- repo: https://github.com/psf/black + rev: 19.10b0 + hooks: + - id: black + language_version: python3 + additional_dependencies: ['click==8.0.4'] +- repo: https://github.com/asottile/blacken-docs + rev: v1.12.0 + hooks: + - id: blacken-docs + additional_dependencies: [black==19.10b0] diff --git a/adidt/__init__.py b/adidt/__init__.py index 17d97669..7bc3c272 100644 --- a/adidt/__init__.py +++ b/adidt/__init__.py @@ -1,8 +1,10 @@ -from adidt.dt import dt -from adidt.clock import clock -from adidt.parts.hmc7044 import hmc7044_dt +from adidt.boards.ad9081_fmc import ad9081_fmc +from adidt.boards.adrv9009_zu11eg import adrv9009_zu11eg +from adidt.boards.daq2 import daq2 from adidt.parts.ad9523_1 import ad9523_1_dt from adidt.parts.ad9545 import ad9545_dt +from adidt.parts.adrv9009 import adrv9009_dt +from adidt.parts.hmc7044 import hmc7044_dt -from adidt.boards.daq2 import daq2 -from adidt.boards.ad9081_fmc import ad9081_fmc +from .clock import clock +from .dt import dt diff --git a/adidt/boards/__init__.py b/adidt/boards/__init__.py index 569e6bdf..21c8f151 100644 --- a/adidt/boards/__init__.py +++ b/adidt/boards/__init__.py @@ -1,2 +1,2 @@ -from adidt.boards.daq2 import daq2 from adidt.boards.ad9081_fmc import ad9081_fmc +from adidt.boards.daq2 import daq2 diff --git a/adidt/boards/ad9081_fmc.py b/adidt/boards/ad9081_fmc.py index b3514208..22a032f3 100644 --- a/adidt/boards/ad9081_fmc.py +++ b/adidt/boards/ad9081_fmc.py @@ -1,6 +1,7 @@ -from .layout import layout import numpy as np +from .layout import layout + class ad9081_fmc(layout): """AD9081 FMC board layout map for clocks and DSP""" @@ -119,8 +120,8 @@ def map_clocks_to_board_layout(self, cfg): ccfg = {"map": map, "clock": cfg["clock"]} fpga = {} - fpga['fpga_adc'] = cfg["fpga_adc"] - fpga['fpga_dac'] = cfg["fpga_dac"] + fpga["fpga_adc"] = cfg["fpga_adc"] + fpga["fpga_dac"] = cfg["fpga_dac"] # Check all clocks are mapped # FIXME diff --git a/adidt/boards/adrv9009_zu11eg.py b/adidt/boards/adrv9009_zu11eg.py new file mode 100644 index 00000000..d98d6b0d --- /dev/null +++ b/adidt/boards/adrv9009_zu11eg.py @@ -0,0 +1,202 @@ +import os + +import numpy as np + +from ..parts.adrv9009 import parse_profile +from .layout import layout + + +def coefs_to_long_string(coefs): + """Convert coefficient array to string. + + Args: + coefs (list): Coefficients. + + Returns: + str: Coefficients as a string. + """ + result = "" + for coef in coefs.split("\n"): + coef = coef.replace(" ", "") + result += f"({coef}) " + return result[:-1] + + +class adrv9009_zu11eg(layout): + """ADRV9009-ZU11EG SOM board layout map for clocks and DSP""" + + clock = "HMC7044" + + adc = "adrv9009_rx" + dac = "adrv9009_tx" + + template_filename = "adrv9009_zu11eg.dts" + output_filename = "adrv9009_zu11eg_out.dts" + + profile = None + + def gen_dt_preprocess(self): + """Preprocess profile for transceiver. + + Args: + profile (dict): Profile. + + Returns: + dict: Preprocessed profile. + """ + if self.profile is None: + raise Exception("Profile not loaded") + rx = self.profile["rx"] + tx = self.profile["tx"] + orx = self.profile["obsRx"] + lpbk = self.profile["lpbk"] + clocks = self.profile["clocks"] + + rx["rxAdcProfile"]["coefs"] = coefs_to_long_string(rx["rxAdcProfile"]["#text"]) + rx["filter"]["coefs"] = coefs_to_long_string(rx["filter"]["#text"]) + + orx["filter"]["coefs"] = coefs_to_long_string(orx["filter"]["#text"]) + orx["orxBandPassAdcProfile"]["coefs"] = coefs_to_long_string( + orx["orxBandPassAdcProfile"]["#text"] + ) + orx["orxLowPassAdcProfile"]["coefs"] = coefs_to_long_string( + orx["orxLowPassAdcProfile"]["#text"] + ) + + tx["filter"]["coefs"] = coefs_to_long_string(tx["filter"]["#text"]) + lpbk["lpbkAdcProfile"]["coefs"] = coefs_to_long_string( + lpbk["lpbkAdcProfile"]["#text"] + ) + + return {"rx": rx, "tx": tx, "orx": orx, "lpbk": lpbk, "clocks": clocks} + + def make_ints(self, cfg, keys): + """Convert keys in a dict to integers. + + Args: + cfg (dict): Configuration. + keys (list): Keys to convert. + + Returns: + dict: Configuration with keys converted to integers. + """ + for key in keys: + if isinstance(cfg[key], float) and cfg[key].is_integer(): + cfg[key] = int(cfg[key]) + return cfg + + def map_jesd_structs(self, cfg): + """Map JIF configuration to integer structs. + + Args: + cfg (dict): JIF configuration. + + Returns: + dict: ADC JESD structs. + dict: DAC JESD structs. + """ + adc = cfg["converter"] + adc["jesd"] = cfg["jesd_adc"] + adc["jesd"]["jesd_class_int"] = self.map_jesd_subclass( + adc["jesd"]["jesd_class"] + ) + dac = cfg["converter"].copy() + dac["jesd"] = cfg["jesd_dac"] + dac["jesd"]["jesd_class_int"] = self.map_jesd_subclass( + dac["jesd"]["jesd_class"] + ) + + adc["jesd"] = self.make_ints(adc["jesd"], ["converter_clock", "sample_clock"]) + dac["jesd"] = self.make_ints(dac["jesd"], ["converter_clock", "sample_clock"]) + + adc["datapath"] = cfg["datapath_adc"] + dac["datapath"] = cfg["datapath_dac"] + + return adc, dac + + def map_clocks_to_board_layout(self, cfg): + """Map JIF configuration to board clock connection layout. + + Args: + cfg (dict): JIF configuration. + + Returns: + dict: Board clock connection layout. + """ + # Fix ups + for key in ["vco", "vcxo"]: + if isinstance(cfg["clock"][key], float) and cfg["clock"][key].is_integer(): + cfg["clock"][key] = int(cfg["clock"][key]) + + map = {} + clk = cfg["clock"]["output_clocks"] + + # Common + map["DEV_REFCLK"] = { + "source_port": 2, + "divider": clk["AD9081_ref_clk"]["divider"], + } + map["DEV_SYSREF"] = { + "source_port": 3, + "divider": np.max( + [clk["adc_sysref"]["divider"], clk["dac_sysref"]["divider"]] + ), + } + map["FPGA_SYSREF"] = { + "source_port": 13, + "divider": np.max( + [clk["adc_fpga_ref_clk"]["divider"], clk["dac_fpga_ref_clk"]["divider"]] + ), + } + + # RX side + map["CORE_CLK_RX"] = { + "source_port": 0, + "divider": clk["adc_fpga_ref_clk"]["divider"], + } + map["CORE_CLK_RX_ALT"] = { + "source_port": 10, + "divider": clk["adc_fpga_ref_clk"]["divider"] * 2, + } + map["FPGA_REFCLK1"] = { + "source_port": 8, + "divider": clk["adc_fpga_ref_clk"]["divider"], + } + + # Tx side + map["CORE_CLK_TX"] = { + "source_port": 6, + "divider": clk["dac_fpga_ref_clk"]["divider"], + } + map["FPGA_REFCLK2"] = { + "source_port": 12, + "divider": clk["dac_fpga_ref_clk"]["divider"], + } + + ccfg = {"map": map, "clock": cfg["clock"]} + + fpga = {} + fpga["fpga_adc"] = cfg["fpga_adc"] + fpga["fpga_dac"] = cfg["fpga_dac"] + + # Check all clocks are mapped + # FIXME + + # Check no source_port is mapped to more than one clock + # FIXME + adc, dac = self.map_jesd_structs(cfg) + + return ccfg, adc, dac, fpga + + def parse_profile(self, filename): + """Parse a profile file. + + Args: + filename (str): Profile file name. + + Returns: + dict: Profile configuration. + """ + if not os.path.exists(filename): + raise Exception(f"Profile file not found: {filename}") + self.profile = parse_profile(filename) diff --git a/adidt/boards/daq2.py b/adidt/boards/daq2.py index 765d10a3..421eb8e7 100644 --- a/adidt/boards/daq2.py +++ b/adidt/boards/daq2.py @@ -1,4 +1,3 @@ - from .layout import layout diff --git a/adidt/boards/layout.py b/adidt/boards/layout.py index 1d49b670..18628e77 100644 --- a/adidt/boards/layout.py +++ b/adidt/boards/layout.py @@ -1,6 +1,7 @@ -from jinja2 import Environment, FileSystemLoader import os +from jinja2 import Environment, FileSystemLoader + class layout: """Common Layout Class for DT generation templates.""" @@ -10,6 +11,9 @@ class layout: template_filename = None output_filename = None + # def gen_dt_preprocess(self, **kwargs): + # return kwargs + def gen_dt(self, **kwargs): """Generate the DT file from configuration structs. @@ -34,6 +38,8 @@ def gen_dt(self, **kwargs): loc = os.path.join(self.template_filename) template = env.get_template(loc) + + kwargs = self.gen_dt_preprocess(**kwargs) output = template.render(**kwargs) with open(self.output_filename, "w") as f: diff --git a/adidt/cli/helpers.py b/adidt/cli/helpers.py index bdc2379b..70c9f321 100644 --- a/adidt/cli/helpers.py +++ b/adidt/cli/helpers.py @@ -1,6 +1,6 @@ +from rich import box from rich.console import Console from rich.table import Column, Table -from rich import box from rich.text import Text console = Console() diff --git a/adidt/cli/main.py b/adidt/cli/main.py index 2d3ae565..11f8dde2 100644 --- a/adidt/cli/main.py +++ b/adidt/cli/main.py @@ -1,15 +1,13 @@ import adidt -import fdt import click -from .helpers import list_node_props, list_node_prop, list_node_subnodes +import fdt + +from .helpers import list_node_prop, list_node_props, list_node_subnodes @click.group() @click.option( - "--no-color", - "-nc", - is_flag=True, - help="Disable formatting", + "--no-color", "-nc", is_flag=True, help="Disable formatting", ) @click.option( "--context", @@ -68,10 +66,7 @@ def cli(ctx, no_color, context, ip, username, password, arch): @click.argument("prop", required=False) @click.argument("value", required=False) @click.option( - "--reboot", - "-r", - is_flag=True, - help="Reboot boards after successful write", + "--reboot", "-r", is_flag=True, help="Reboot boards after successful write", ) @click.option( "--compat", @@ -164,22 +159,13 @@ def prop(ctx, node_name, prop, value, reboot, compat, children): @cli.command() @click.argument("rd", required=False) @click.option( - "--reboot", - "-r", - is_flag=True, - help="Reboot boards after successful write", + "--reboot", "-r", is_flag=True, help="Reboot boards after successful write", ) @click.option( - "--show", - "-s", - is_flag=True, - help="Print commands as run", + "--show", "-s", is_flag=True, help="Print commands as run", ) @click.option( - "--dry-run", - "-d", - is_flag=True, - help="Dryrun, do not run commands", + "--dry-run", "-d", is_flag=True, help="Dryrun, do not run commands", ) @click.pass_context def sd_move(ctx, rd, reboot, show, dry_run): @@ -214,22 +200,13 @@ def sd_move(ctx, rd, reboot, show, dry_run): @cli.command() @click.argument("files", required=False) @click.option( - "--reboot", - "-r", - is_flag=True, - help="Reboot boards after successful write", + "--reboot", "-r", is_flag=True, help="Reboot boards after successful write", ) @click.option( - "--show", - "-s", - is_flag=True, - help="Print commands as run", + "--show", "-s", is_flag=True, help="Print commands as run", ) @click.option( - "--dry-run", - "-d", - is_flag=True, - help="Dryrun, do not run commands", + "--dry-run", "-d", is_flag=True, help="Dryrun, do not run commands", ) @click.pass_context def sd_remote_copy(ctx, files, reboot, show, dry_run): @@ -271,22 +248,13 @@ def sd_remote_copy(ctx, files, reboot, show, dry_run): help="Use node name to check against compatible id of node during search. This is only used for the first node", ) @click.option( - "--reboot", - "-r", - is_flag=True, - help="Reboot boards after successful write", + "--reboot", "-r", is_flag=True, help="Reboot boards after successful write", ) @click.option( - "--prop", - "-p", - default=None, - help="Property of node to read to set", + "--prop", "-p", default=None, help="Property of node to read to set", ) @click.option( - "--value", - "-v", - default=None, - help="Value to set property to", + "--value", "-v", default=None, help="Value to set property to", ) @click.pass_context def props(ctx, node_name, compat, reboot, prop, value): @@ -406,10 +374,7 @@ def print_node(node): type=click.Choice(["clock", "converter", "system", "fpga"]), ) @click.option( - "--reboot", - "-r", - is_flag=True, - help="Reboot boards after successful write", + "--reboot", "-r", is_flag=True, help="Reboot boards after successful write", ) @click.option( "--filename", diff --git a/adidt/clock.py b/adidt/clock.py index b58842f3..6296c44d 100644 --- a/adidt/clock.py +++ b/adidt/clock.py @@ -1,6 +1,5 @@ -import adidt.dt as dt -import adidt.parts as parts - +from .dt import dt +import .parts as parts class clock(dt): supported_parts = ["HMC7044", "AD9523-1", "AD9545"] diff --git a/adidt/dt.py b/adidt/dt.py index 4ba53c95..1569dc1b 100644 --- a/adidt/dt.py +++ b/adidt/dt.py @@ -1,11 +1,13 @@ """Device tree interface class.""" -import fdt -from fabric import Connection, Config +import os +import os.path import random import string -import os.path -import os -from adidt.sd import sd + +import fdt +from fabric import Config, Connection + +from .sd import sd class dt(sd): @@ -51,10 +53,7 @@ def __init__( if "remote" in self.dt_source: self._con = Connection( "{username}@{ip}:{port}".format( - username=username, - ip=ip, - port=22, - connect_timeout=5, + username=username, ip=ip, port=22, connect_timeout=5, ), connect_kwargs={"password": password}, ) @@ -68,7 +67,7 @@ def __init__( self._set_arch(arch) self._import_sysfs() elif dt_source == "local_file": - if arch=="auto": + if arch == "auto": raise Exception("arch must be set when using local_file mode") self._import_file() else: diff --git a/adidt/parts/__init__.py b/adidt/parts/__init__.py index 31adf993..aebcaa29 100644 --- a/adidt/parts/__init__.py +++ b/adidt/parts/__init__.py @@ -1,3 +1,3 @@ -from adidt.parts.hmc7044 import hmc7044_dt from adidt.parts.ad9523_1 import ad9523_1_dt from adidt.parts.ad9545 import ad9545_dt +from adidt.parts.hmc7044 import hmc7044_dt diff --git a/adidt/parts/ad9523_1.py b/adidt/parts/ad9523_1.py index 2869ad75..a914f286 100644 --- a/adidt/parts/ad9523_1.py +++ b/adidt/parts/ad9523_1.py @@ -1,6 +1,7 @@ +import math from typing import Dict + import fdt -import math import numpy as np from adidt.parts.clock_dt import clock_dt diff --git a/adidt/parts/ad9545.py b/adidt/parts/ad9545.py index ce3acd0a..152b3b83 100644 --- a/adidt/parts/ad9545.py +++ b/adidt/parts/ad9545.py @@ -1,6 +1,7 @@ from typing import Dict -from adidt.dt import dt + import fdt +from adidt.dt import dt class ad9545_dt(dt): @@ -32,9 +33,7 @@ def pll_set_rate(self, pll_nr: int, rate: int, node: fdt.Node): clock_pos = i if clock_pos == -1: - raise Exception( - "AD9545: missing PLL" + str(pll_nr) + " in assigned-clocks" - ) + raise Exception("AD9545: missing PLL" + str(pll_nr) + " in assigned-clocks") assigned_clock_rates_prop = node.get_property("assigned-clock-rates") assigned_clock_rates = list(assigned_clock_rates_prop) @@ -95,13 +94,9 @@ def set_source_priorities_from_config(self, node: fdt.Node, config: Dict): priority_attr = "priority_source_" + str(adi_pll_source_nr) if priority_attr in config[pll_name]: new_priority = config[pll_name][priority_attr] - pll_profile_node.set_property( - "adi,profile-priority", - new_priority - ) + pll_profile_node.set_property("adi,profile-priority", new_priority) - def set_dt_node_from_config(self, node: fdt.Node, - config: Dict, append=False): + def set_dt_node_from_config(self, node: fdt.Node, config: Dict, append=False): """Set AD9545 node from JIF configuration Args: @@ -119,9 +114,7 @@ def set_dt_node_from_config(self, node: fdt.Node, if r_div != 0: ref_node = node.get_subnode("ref-input-clk@" + str(i)) if ref_node is None: - raise Exception( - "AD9545: missing node: ref-input-clk@" + str(i) - ) + raise Exception("AD9545: missing node: ref-input-clk@" + str(i)) ref_node.set_property("adi,r-divider-ratio", r_div) @@ -146,7 +139,7 @@ def set_dt_node_from_config(self, node: fdt.Node, continue pll_dict = config["PLL" + str(i)] - hitless_enable = ("hitless" in pll_dict) + hitless_enable = "hitless" in pll_dict pll_node = node.get_subnode("pll-clk@" + str(i)) if hitless_enable: @@ -155,34 +148,26 @@ def set_dt_node_from_config(self, node: fdt.Node, fb_source_rate = hitless_dict["fb_source_rate"] pll_node.set_property( - "adi,pll-internal-zero-delay-feedback", - fb_source_nr, + "adi,pll-internal-zero-delay-feedback", fb_source_nr, ) pll_node.set_property( - "adi,pll-internal-zero-delay-feedback-hz", - fb_source_rate, + "adi,pll-internal-zero-delay-feedback-hz", fb_source_rate, ) # set slew rate limit to 4 ms / s during hitless phase acq pll_node.set_property( - "adi,pll-slew-rate-limit-ps", - 4000000000, + "adi,pll-slew-rate-limit-ps", 4000000000, ) else: try: - pll_node.remove_property( - "adi,pll-internal-zero-delay-feedback" - ) + pll_node.remove_property("adi,pll-internal-zero-delay-feedback") - pll_node.remove_property( - "adi,pll-internal-zero-delay-feedback-hz" - ) + pll_node.remove_property("adi,pll-internal-zero-delay-feedback-hz") # set slew rate limit to default value during PBO mode pll_node.set_property( - "adi,pll-slew-rate-limit-ps", - 100000000, + "adi,pll-slew-rate-limit-ps", 100000000, ) except ValueError: pass diff --git a/adidt/parts/adrv9009.py b/adidt/parts/adrv9009.py new file mode 100644 index 00000000..0a4c54f5 --- /dev/null +++ b/adidt/parts/adrv9009.py @@ -0,0 +1,206 @@ +import math +from typing import Dict + +import fdt +import xmltodict +from adidt.dt import dt + + +def handle_ints(val): + val = int(val) + # Handles negative numbers + return int(hex((val + (1 << 32)) % (1 << 32)), 16) + + +def parse_profile(filename): + # Update profile to non-shitty xml + with open(filename) as sxmlfile: + sxmldata = sxmlfile.read() + # Correct each header + outfile = [] + for line in sxmldata.split("\n"): + if "<" in line and "")] + # Fix all prop=val + if "," in within: + oline = [] + # print(line) + for index, sline in enumerate(line.split(" ")): + if index == 3: + sline = f"Bandwidth={sline}" + if index in [4, 6]: + sline = sline + "=" + oline += [sline] + line = " ".join(oline) + line = line.replace(",", "") + line = line.replace("= ", "=") + if " " in within: + # print(line) + oline = [] + spaces = 0 + for sline in line.split(" "): + if len(sline) > 0: + starter = sline + " " + break + spaces += 1 + for sline in line.split(" "): + if ( + len(oline) == 0 + and len(sline) + and "<" not in sline + and ">" not in sline + and "=" not in sline + ): + oline += [f'type="{sline}"'] + if "=" in sline: + sline = sline.replace(">", "") + sline = sline.replace("<", "") + p = sline.split("=")[0] + v = sline.split("=")[1] + # print(f"{p}={v}") + oline += [f'{p}="{v}"'] + + # print(" "*spaces+starter+" ".join(oline)+">") + outfile += [" " * spaces + starter + " ".join(oline) + ">"] + else: + # print(line) + if "=" in line: + s1 = line.find("<") + s2 = line.find(">") + within = line[s1 + 1 : s2] + o = within.split("=") + ss = f"{o[0]}>{o[1]}; + # node.set_property("adi,rx-settings-rx-channels = <3>; + + def set_dt_node_from_config( + self, node: fdt.Node, config: Dict, profile: Dict, append=False + ): + """Set ADRV9009 node from JIF configuration + + Args: + node (fdt.Node): Device tree parent node of adrv9009 + config (Dict): Configuration struct generated from JIF + append (boolean): Enable appending to subnode, if false the existing are removed + """ + + # Add profile fields + self._add_rx_profile_fields(node, profile) diff --git a/adidt/parts/clock_dt.py b/adidt/parts/clock_dt.py index 677f1183..31f4f095 100644 --- a/adidt/parts/clock_dt.py +++ b/adidt/parts/clock_dt.py @@ -1,7 +1,8 @@ -from adidt import parts +import math from typing import Dict + import fdt -import math +from adidt import parts class clock_dt: @@ -63,6 +64,7 @@ def get_node_by_prop(self, parent, prop, value): Returns: A list containing all nodes refered to in the "clocks" phandle array """ + def get_used_clocks(self, node): used_clocks = [] clocks_prop = node.get_property("clocks") @@ -72,7 +74,7 @@ def get_used_clocks(self, node): # first value in "clocks" property is a phandle # next phandle is located after "#clock-cells"+1 positions i = 0 - while (i < len(clocks_val)): + while i < len(clocks_val): clock_phandle = clocks_val[i] phandle_props = self._dt.search("phandle") diff --git a/adidt/parts/hmc7044.py b/adidt/parts/hmc7044.py index 98f7832f..a4987ddf 100644 --- a/adidt/parts/hmc7044.py +++ b/adidt/parts/hmc7044.py @@ -1,58 +1,59 @@ -from typing import Dict -from adidt.dt import dt -import fdt import math +from typing import Dict +import fdt +from adidt.dt import dt from adidt.parts.clock_dt import clock_dt + class hmc7044_dt(dt, clock_dt): """HMC7044 Device tree map class.""" pulse_gen_modes = { - "GEN_LEVEL_SENSITIVE" : 0, - "GEN_1_PULSE" : 1, - "GEN_2_PULSE" : 2, - "GEN_4_PULSE" : 3, - "GEN_8_PULSE" : 4, - "GEN_16_PULSE" : 5, - "GEN_CONT_PULSE" : 7, + "GEN_LEVEL_SENSITIVE": 0, + "GEN_1_PULSE": 1, + "GEN_2_PULSE": 2, + "GEN_4_PULSE": 3, + "GEN_8_PULSE": 4, + "GEN_16_PULSE": 5, + "GEN_CONT_PULSE": 7, } driver_modes = { - "CML" : 0, - "LVPECL" : 1, - "LVDS" : 2, - "CMOS" : 3, + "CML": 0, + "LVPECL": 1, + "LVDS": 2, + "CMOS": 3, } driver_impedances = { - "DISABLE" : 0, - "100_OHM" : 1, - "50_OHM" : 3, + "DISABLE": 0, + "100_OHM": 1, + "50_OHM": 3, } output_mux_modes = { - "CH_DIV" : 0, - "ANALOG_DELAY" : 1, - "GROUP_PAIR" : 3, - "VCO_CLOCK" : 4, + "CH_DIV": 0, + "ANALOG_DELAY": 1, + "GROUP_PAIR": 3, + "VCO_CLOCK": 4, } cmos_outputs_reg_field_map = { - 0 : {"P" : 1, "N" : 0}, - 1 : {"P" : 0, "N" : 1}, - 2 : {"P" : 0, "N" : 1}, - 3 : {"P" : 1, "N" : 0}, - 4 : {"P" : 0, "N" : 1}, - 5 : {"P" : 1, "N" : 0}, - 6 : {"P" : 1, "N" : 0}, - 7 : {"P" : 0, "N" : 1}, - 8 : {"P" : 0, "N" : 1}, - 9 : {"P" : 1, "N" : 0}, - 10 : {"P" : 1, "N" : 0}, - 11 : {"P" : 0, "N" : 1}, - 12 : {"P" : 0, "N" : 1}, - 13 : {"P" : 1, "N" : 0}, + 0: {"P": 1, "N": 0}, + 1: {"P": 0, "N": 1}, + 2: {"P": 0, "N": 1}, + 3: {"P": 1, "N": 0}, + 4: {"P": 0, "N": 1}, + 5: {"P": 1, "N": 0}, + 6: {"P": 1, "N": 0}, + 7: {"P": 0, "N": 1}, + 8: {"P": 0, "N": 1}, + 9: {"P": 1, "N": 0}, + 10: {"P": 1, "N": 0}, + 11: {"P": 0, "N": 1}, + 12: {"P": 0, "N": 1}, + 13: {"P": 1, "N": 0}, } def set_clock_node(self, parent, clk, name, reg): @@ -65,36 +66,38 @@ def set_clock_node(self, parent, clk, name, reg): driver_mode = self.driver_modes[clk["driver-mode"]] node.append(fdt.PropWords("adi,driver-mode", driver_mode)) - if ("high-performance-mode-disable" in clk): + if "high-performance-mode-disable" in clk: node.append(fdt.Property("adi,high-performance-mode-disable")) - if ("startup-mode-dynamic-enable" in clk): + if "startup-mode-dynamic-enable" in clk: node.append(fdt.Property("adi,startup-mode-dynamic-enable")) - if ("dynamic-driver-enable" in clk): + if "dynamic-driver-enable" in clk: node.append(fdt.Property("adi,dynamic-driver-enable")) - if ("force-mute-enable" in clk): + if "force-mute-enable" in clk: node.append(fdt.Property("adi,force-mute-enable")) - if ("output-mux-mode" in clk): + if "output-mux-mode" in clk: mux_mode = self.output_mux_modes[clk["output-mux-mode"]] node.append(fdt.PropWords("adi,output-mux-mode", mux_mode)) - if ("driver-impedance-mode" in clk): + if "driver-impedance-mode" in clk: impedance_mode = self.driver_impedances[clk["driver-impedance-mode"]] node.append(fdt.PropWords("adi,driver-impedance-mode", impedance_mode)) - if ("fine-delay" in clk): + if "fine-delay" in clk: node.append(fdt.PropWords("adi,fine-analog-delay", clk["fine-delay"])) - if ("coarse-delay" in clk): + if "coarse-delay" in clk: node.append(fdt.PropWords("adi,coarse-digital-delay", clk["coarse-delay"])) # in CMOS mode, the impedance property describes the output status - if ("CMOS" in clk): - prop_val = (clk["CMOS"]["P"] << self.cmos_outputs_reg_field_map[reg]["P"]) - propval = prop_val | (clk["CMOS"]["N"] << self.cmos_outputs_reg_field_map[reg]["N"]) + if "CMOS" in clk: + prop_val = clk["CMOS"]["P"] << self.cmos_outputs_reg_field_map[reg]["P"] + propval = prop_val | ( + clk["CMOS"]["N"] << self.cmos_outputs_reg_field_map[reg]["N"] + ) node.append(fdt.PropWords("adi,driver-impedance-mode", prop_val)) parent.append(node) @@ -130,18 +133,20 @@ def set_dt_node_from_config(self, node: fdt.Node, config: Dict, append=False): i += 1 continue - used_clock.set_property("clock-frequency", clock["reference_frequencies"][i]) + used_clock.set_property( + "clock-frequency", clock["reference_frequencies"][i] + ) i += 1 # Set reference selection priorities - if ("reference_selection_order" in clock): + if "reference_selection_order" in clock: ref_order_val = 0 priority = 0 # MSB (Fourth priority input [1:0]) .... (First priority input [1:0]) LSB for ref_nr in clock["reference_selection_order"]: - if (ref_nr >= 4): + if ref_nr >= 4: raise Exception("Reference number:" + str(ref_nr) + " invalid.") - ref_order_val |= (ref_nr << (priority * 2)) + ref_order_val |= ref_nr << (priority * 2) priority += 1 node.set_property("adi,pll1-ref-prio-ctrl", ref_order_val) diff --git a/adidt/sd.py b/adidt/sd.py index 5def3212..6f1bdc14 100644 --- a/adidt/sd.py +++ b/adidt/sd.py @@ -1,9 +1,10 @@ """ADI SD Card Manipulation Utilies""" -import click import os - from typing import List +import click + + class sd: def find(self, loc, ext=None): if ext: @@ -21,7 +22,9 @@ def list(self, loc, ext=None): out = out.stdout.split("\n") return list(filter(lambda c: "*" not in c and c != "", out)) - def copy_local_files_to_remote_sd_card(self, files: List[str], show=False, dryrun=False): + def copy_local_files_to_remote_sd_card( + self, files: List[str], show=False, dryrun=False + ): # Check if local files exist for f in files: if not os.path.exists(f): @@ -34,7 +37,7 @@ def copy_local_files_to_remote_sd_card(self, files: List[str], show=False, dryru if show: print(f"scp {file} {folder}/") if not dryrun: - self._con.put(file, remote=folder+"/") + self._con.put(file, remote=folder + "/") finally: self._runr(f"umount /dev/mmcblk0p1") self._runr(f"rm -rf {folder}") diff --git a/adidt/templates/adrv9009_zu11eg.dts b/adidt/templates/adrv9009_zu11eg.dts new file mode 100644 index 00000000..fb22969c --- /dev/null +++ b/adidt/templates/adrv9009_zu11eg.dts @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ADRV2CRR-FMC using ADRV9009-ZU11EG Rev.B System on Module (200.000 MSPS) + * + * https://wiki.analog.com/resources/eval/user-guides/adrv9009 + * https://wiki.analog.com/resources/tools-software/linux-drivers/iio-transceiver/adrv9009 + * https://wiki.analog.com/resources/tools-software/linux-software/adrv9009_advanced_plugin + * https://wiki.analog.com/resources/eval/user-guides/adrv9009-zu11eg/adrv2crr-fmc_carrier_board + * + * hdl_project: + * board_revision: + * + * Copyright (C) 2022 Analog Devices Inc. + */ + +#include "zynqmp-adrv9009-zu11eg-revb-adrv2crr-fmc-revb-jesd204-fsm.dts" +#include + +&hmc7044 { + // hmc7044_c4: channel@4 { + // adi,divider = <24>; // 100000000 + // }; + + + // adi,vcxo-frequency = <122880000>; + // adi,pll1-clkin-frequencies = <30720000 30720000 0 0>; + adi,vcxo-frequency = <100000000>; + adi,pll1-clkin-frequencies = <0 31250000 0 0>; + adi,pll2-output-frequency = <2400000000>; /* VCO @ 2.400GHz */ + + hmc7044_c0: channel@0 { + reg = <0>; + adi,extended-name = "DEV_REFCLK_A"; // TRX device clk + adi,divider = <60>; + adi,driver-mode = ; // LVDS + adi,coarse-digital-delay = <15>; + }; + hmc7044_c1: channel@1 { + reg = <1>; + adi,extended-name = "DEV_SYSREF_A"; // TRX sysref clk + adi,divider = <3840>; + adi,driver-mode = ; // LVDS + adi,startup-mode-dynamic-enable; + adi,high-performance-mode-disable; + adi,driver-impedance-mode = ; + }; + hmc7044_c2: channel@2 { + reg = <2>; + adi,extended-name = "DEV_REFCLK_B"; // TRX2 device clk + adi,divider = <60>; + adi,driver-mode = ; // LVDS + adi,coarse-digital-delay = <15>; + }; + hmc7044_c3: channel@3 { + reg = <3>; + adi,extended-name = "DEV_SYSREF_B"; // TRX2 sysref clk + adi,divider = <3840>; + adi,driver-mode = ; // LVDS + adi,startup-mode-dynamic-enable; + adi,high-performance-mode-disable; + adi,driver-impedance-mode = ; + }; + hmc7044_c4: channel@4 { + reg = <4>; + adi,extended-name = "JESD_REFCLK_TX_OBS_AB"; // FPGA Transceiver reference (QPLL) + adi,divider = <60>; // 245760000 + adi,driver-mode = ; // LVDS + }; + hmc7044_c5: channel@5 { + reg = <5>; + adi,extended-name = "JESD_REFCLK_RX_AB"; // FPGA Transceiver reference (RX-side CPLL usually) + adi,divider = <60>; // 245760000 + adi,driver-mode = ; // LVDS + }; + hmc7044_c6: channel@6 { + reg = <6>; + adi,extended-name = "CORE_CLK_TX_OBS_AB"; // FPGA Transceiver link clock (shared between TX and OBS) + adi,divider = <60>; // 122880000 + adi,driver-mode = ; // LVDS + }; + hmc7044_c7: channel@7 { + reg = <7>; + adi,extended-name = "CORE_CLK_RX_AB"; // FPGA Transceiver link clock (shared between RX) + adi,divider = <60>; // 245760000 + adi,driver-mode = ; // LVDS + }; + hmc7044_c8: channel@8 { + reg = <8>; + adi,extended-name = "FPGA_SYSREF_TX_OBS_AB"; // FPGA sysref TX/OBS + adi,divider = <3840>; // 768000 + adi,driver-mode = ; // LVDS + adi,startup-mode-dynamic-enable; + adi,high-performance-mode-disable; + }; + hmc7044_c9: channel@9 { + reg = <9>; + adi,extended-name = "FPGA_SYSREF_RX_AB"; // FPGA sysref RX + adi,divider = <3840>; // 768000 + adi,driver-mode = ; // LVDS + adi,startup-mode-dynamic-enable; + adi,high-performance-mode-disable; + }; + +}; + +&hmc7044_car { + adi,pll2-output-frequency = <2400000000>; /* VCO @ 2.400GHz */ +}; + +&axi_adrv9009_adxcvr_tx { + adi,sys-clk-select = ; /* Switch to QPLL1 */ +}; + +&axi_adrv9009_adxcvr_rx { + adi,out-clk-select = ; +}; + +&trx0_adrv9009 { + adi,rx-profile-rf-bandwidth_hz = <{{ rx['rfBandwidth_Hz'] }}>; + adi,rx-profile-rhb1-decimation = <{{ rx['rhb1Decimation'] }}>; + adi,rx-profile-rx-bbf3d-bcorner_khz = <{{ rx['rxBbf3dBCorner_kHz'] }}>; + adi,rx-profile-rx-ddc-mode = <{{ rx['rxDdcMode'] }}>; + adi,rx-profile-rx-dec5-decimation = <{{ rx['rxDec5Decimation'] }}>; + adi,rx-profile-rx-fir-decimation = <{{ rx['rxFirDecimation'] }}>; + adi,rx-profile-rx-fir-gain_db = <{{ rx['filter']['@gain_dB'] }}>; + adi,rx-profile-rx-fir-num-fir-coefs = <{{ rx['filter']['@numFirCoefs'] }}>; + adi,rx-profile-rx-output-rate_khz = <{{ rx['rxOutputRate_kHz'] }}>; + adi,rx-profile-rx-fir-coefs = /bits/ 16 <{{ rx['filter']['coefs'] }}>; + adi,rx-profile-rx-adc-profile = /bits/ 16 <{{ rx['rxAdcProfile']['coefs'] }}>; + + adi,orx-profile-orx-ddc-mode = <{{ orx['orxDdcMode'] }}>; + adi,orx-profile-orx-output-rate_khz = <{{ orx['orxOutputRate_kHz'] }}>; + adi,orx-profile-rf-bandwidth_hz = <{{ orx['rfBandwidth_Hz'] }}>; + adi,orx-profile-rhb1-decimation = <{{ orx['rhb1Decimation'] }}>; + adi,orx-profile-rx-bbf3d-bcorner_khz = <{{ orx['rxBbf3dBCorner_kHz'] }}>; + adi,orx-profile-rx-dec5-decimation = <{{ orx['rxDec5Decimation'] }}>; + adi,orx-profile-rx-fir-decimation = <{{ orx['rxFirDecimation'] }}>; + adi,orx-profile-rx-fir-gain_db = <{{ orx['filter']['@gain_dB'] }}>; + adi,orx-profile-rx-fir-num-fir-coefs = <{{ orx['filter']['@numFirCoefs'] }}>; + adi,orx-profile-rx-fir-coefs = /bits/ 16 <{{ orx['filter']['coefs'] }}>; + adi,orx-profile-orx-low-pass-adc-profile = /bits/ 16 <{{ orx['orxLowPassAdcProfile']['coefs'] }}>; + adi,orx-profile-orx-band-pass-adc-profile = /bits/ 16 <{{ orx['orxBandPassAdcProfile']['coefs'] }}>; + + adi,tx-profile-dac-div = <{{ tx['dacDiv'] }}>; + adi,tx-profile-primary-sig-bandwidth_hz = <{{ tx['primarySigBandwidth_Hz'] }}>; + adi,tx-profile-rf-bandwidth_hz = <{{ tx['rfBandwidth_Hz'] }}>; + adi,tx-profile-thb1-interpolation = <{{ tx['thb1Interpolation'] }}>; + adi,tx-profile-thb2-interpolation = <{{ tx['thb2Interpolation'] }}>; + adi,tx-profile-thb3-interpolation = <{{ tx['thb3Interpolation'] }}>; + adi,tx-profile-tx-bbf3d-bcorner_khz = <{{ tx['txBbf3dBCorner_kHz'] }}>; + adi,tx-profile-tx-dac3d-bcorner_khz = <{{ tx['txDac3dBCorner_kHz'] }}>; + adi,tx-profile-tx-fir-gain_db = <{{ tx['filter']['@gain_dB'] }}>; + adi,tx-profile-tx-fir-interpolation = <{{ tx['txFirInterpolation'] }}>; + adi,tx-profile-tx-fir-num-fir-coefs = <{{ tx['filter']['@numFirCoefs'] }}>; + adi,tx-profile-tx-input-rate_khz = <{{ tx['txInputRate_kHz'] }}>; + adi,tx-profile-tx-int5-interpolation = <{{ tx['txInt5Interpolation'] }}>; + adi,tx-profile-tx-fir-coefs = /bits/ 16 <{{ tx['filter']['coefs'] }}>; + adi,tx-profile-loop-back-adc-profile = /bits/ 16 <{{ tx['filter']['coefs'] }}>; + + adi,dig-clocks-clk-pll-hs-div = <{{ clocks['clkPllHsDiv'] }}>; + adi,dig-clocks-clk-pll-vco-freq_khz = <{{ clocks['clkPllVcoFreq_kHz'] }}>; + adi,dig-clocks-device-clock_khz = <{{ clocks['deviceClock_kHz'] }}>; +}; + +&trx1_adrv9009 { + adi,rx-profile-rf-bandwidth_hz = <{{ rx['rfBandwidth_Hz'] }}>; + adi,rx-profile-rhb1-decimation = <{{ rx['rhb1Decimation'] }}>; + adi,rx-profile-rx-bbf3d-bcorner_khz = <{{ rx['rxBbf3dBCorner_kHz'] }}>; + adi,rx-profile-rx-ddc-mode = <{{ rx['rxDdcMode'] }}>; + adi,rx-profile-rx-dec5-decimation = <{{ rx['rxDec5Decimation'] }}>; + adi,rx-profile-rx-fir-decimation = <{{ rx['rxFirDecimation'] }}>; + adi,rx-profile-rx-fir-gain_db = <{{ rx['filter']['@gain_dB'] }}>; + adi,rx-profile-rx-fir-num-fir-coefs = <{{ rx['filter']['@numFirCoefs'] }}>; + adi,rx-profile-rx-output-rate_khz = <{{ rx['rxOutputRate_kHz'] }}>; + adi,rx-profile-rx-fir-coefs = /bits/ 16 <{{ rx['filter']['coefs'] }}>; + adi,rx-profile-rx-adc-profile = /bits/ 16 <{{ rx['rxAdcProfile']['coefs'] }}>; + + adi,orx-profile-orx-ddc-mode = <{{ orx['orxDdcMode'] }}>; + adi,orx-profile-orx-output-rate_khz = <{{ orx['orxOutputRate_kHz'] }}>; + adi,orx-profile-rf-bandwidth_hz = <{{ orx['rfBandwidth_Hz'] }}>; + adi,orx-profile-rhb1-decimation = <{{ orx['rhb1Decimation'] }}>; + adi,orx-profile-rx-bbf3d-bcorner_khz = <{{ orx['rxBbf3dBCorner_kHz'] }}>; + adi,orx-profile-rx-dec5-decimation = <{{ orx['rxDec5Decimation'] }}>; + adi,orx-profile-rx-fir-decimation = <{{ orx['rxFirDecimation'] }}>; + adi,orx-profile-rx-fir-gain_db = <{{ orx['filter']['@gain_dB'] }}>; + adi,orx-profile-rx-fir-num-fir-coefs = <{{ orx['filter']['@numFirCoefs'] }}>; + adi,orx-profile-rx-fir-coefs = /bits/ 16 <{{ orx['filter']['coefs'] }}>; + adi,orx-profile-orx-low-pass-adc-profile = /bits/ 16 <{{ orx['orxLowPassAdcProfile']['coefs'] }}>; + adi,orx-profile-orx-band-pass-adc-profile = /bits/ 16 <{{ orx['orxBandPassAdcProfile']['coefs'] }}>; + + adi,tx-profile-dac-div = <{{ tx['dacDiv'] }}>; + adi,tx-profile-primary-sig-bandwidth_hz = <{{ tx['primarySigBandwidth_Hz'] }}>; + adi,tx-profile-rf-bandwidth_hz = <{{ tx['rfBandwidth_Hz'] }}>; + adi,tx-profile-thb1-interpolation = <{{ tx['thb1Interpolation'] }}>; + adi,tx-profile-thb2-interpolation = <{{ tx['thb2Interpolation'] }}>; + adi,tx-profile-thb3-interpolation = <{{ tx['thb3Interpolation'] }}>; + adi,tx-profile-tx-bbf3d-bcorner_khz = <{{ tx['txBbf3dBCorner_kHz'] }}>; + adi,tx-profile-tx-dac3d-bcorner_khz = <{{ tx['txDac3dBCorner_kHz'] }}>; + adi,tx-profile-tx-fir-gain_db = <{{ tx['filter']['@gain_dB'] }}>; + adi,tx-profile-tx-fir-interpolation = <{{ tx['txFirInterpolation'] }}>; + adi,tx-profile-tx-fir-num-fir-coefs = <{{ tx['filter']['@numFirCoefs'] }}>; + adi,tx-profile-tx-input-rate_khz = <{{ tx['txInputRate_kHz'] }}>; + adi,tx-profile-tx-int5-interpolation = <{{ tx['txInt5Interpolation'] }}>; + adi,tx-profile-tx-fir-coefs = /bits/ 16 <{{ tx['filter']['coefs'] }}>; + adi,tx-profile-loop-back-adc-profile = /bits/ 16 <{{ lpbk['lpbkAdcProfile']['coefs'] }}>; + + adi,dig-clocks-clk-pll-hs-div = <{{ clocks['clkPllHsDiv'] }}>; + adi,dig-clocks-clk-pll-vco-freq_khz = <{{ clocks['clkPllVcoFreq_kHz'] }}>; + adi,dig-clocks-device-clock_khz = <{{ clocks['deviceClock_kHz'] }}>; +}; diff --git a/adidt/templates/daq2.tmpl b/adidt/templates/daq2.tmpl index 19ea8057..3e1d228f 100644 --- a/adidt/templates/daq2.tmpl +++ b/adidt/templates/daq2.tmpl @@ -152,4 +152,4 @@ adi,nco-channel-phase-offset = /bits/ 64 <0>; }; }; -}; \ No newline at end of file +}; diff --git a/docs/access.md b/docs/access.md index 331f265e..3a09cf3f 100644 --- a/docs/access.md +++ b/docs/access.md @@ -30,7 +30,3 @@ adidtc -c local_sysfs props -cp adi,ad9361 adi,rx-synthesizer-frequency-hz 2400000000 ``` - - - - diff --git a/docs/cli.md b/docs/cli.md index 627d83e9..6c317f18 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -8,4 +8,3 @@ This page provides documentation for our command line tools. :command: cli :prog_name: adidtc :style: table - diff --git a/docs/index.md b/docs/index.md index e6e08017..3a71fe6c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -16,4 +16,3 @@ pip install git+https://github.com/analogdevicesinc/pyadi-dt.git - [Reporting bugs](https://github.com/analogdevicesinc/pyadi-dt/issues): GitHub issue tracker - [Support](https://ez.analog.com/sw-interface-tools/f/q-a): EngineerZone Software Interface Tools forum - [DeviceTree Specification](http://devicetree-org.github.io/devicetree-specification): DeviceTree specification and organization reference - diff --git a/examples/daq2_dts_gen.py b/examples/daq2_dts_gen.py index 1b5b9dda..9d43f3ac 100644 --- a/examples/daq2_dts_gen.py +++ b/examples/daq2_dts_gen.py @@ -78,4 +78,4 @@ clock, adc, dac = d2.map_clocks_to_board_layout(cfg) -d2.gen_dt(clock=clock, adc=adc, dac=dac) \ No newline at end of file +d2.gen_dt(clock=clock, adc=adc, dac=dac) diff --git a/requirements/requirements_dev.txt b/requirements/requirements_dev.txt index 723cf0c0..e8c70a2f 100644 --- a/requirements/requirements_dev.txt +++ b/requirements/requirements_dev.txt @@ -1,7 +1,10 @@ +black Click fdt fabric rich numpy pytest -jinja2 \ No newline at end of file +jinja2 +xmltodict +pre-commit diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..6ae616f8 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,29 @@ +[bumpversion] +current_version = 0.0.12 +commit = True +tag = True + +[bumpversion:file:adi/__init__.py] +search = __version__ = "{current_version}" +replace = __version__ = "{new_version}" + +[bumpversion:file:doc/source/conf.py] +search = release = "{current_version}" +replace = release = "{new_version}" + +[isort] +multi_line_output=3 +include_trailing_comma=True +force_grid_wrap=0 +use_parentheses=True +line_length=88 + +[flake8] +ignore = E203, E266, E501, W503, F401, F403 +max-line-length = 88 +max-complexity = 18 +select = B,C,E,F,W,T4 +exclude = test/*,examples/*,doc/*,images/* + +[mypy] +ignore_missing_imports=true diff --git a/setup.py b/setup.py index e7222f8c..e3fbaa4a 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup setup( name="adidt", @@ -7,10 +7,6 @@ include_package_data=True, package_data={"adidt": ["templates/*.tmpl"]}, py_modules=["adidt"], - install_requires=["Click", "fdt", "fabric", "rich", "numpy"], - entry_points={ - "console_scripts": [ - "adidtc = adidt.cli.main:cli", - ], - }, + install_requires=["Click", "fdt", "fabric", "rich", "numpy", "xmltodict"], + entry_points={"console_scripts": ["adidtc = adidt.cli.main:cli",],}, ) diff --git a/test/ad9523_1/test_ad9523_1.py b/test/ad9523_1/test_ad9523_1.py index 3fa8799f..77ef8000 100644 --- a/test/ad9523_1/test_ad9523_1.py +++ b/test/ad9523_1/test_ad9523_1.py @@ -1,7 +1,7 @@ -import pytest import os import adidt as dt +import pytest def test_ad9523_1_add_nodes(): @@ -48,9 +48,9 @@ def test_ad9523_1_add_nodes(): }, } - d.set(config2['clock']['part'], config2['clock']) + d.set(config2["clock"]["part"], config2["clock"]) - config = config2['clock'] + config = config2["clock"] # Checks node = d.get_node_by_compatible("adi,ad9523-1") diff --git a/test/ad9545/test_ad9545.py b/test/ad9545/test_ad9545.py index d607b424..bb568790 100644 --- a/test/ad9545/test_ad9545.py +++ b/test/ad9545/test_ad9545.py @@ -1,4 +1,5 @@ import os + import adidt as dt @@ -9,21 +10,18 @@ def test_ad9545_add_nodes(): d = dt.ad9545_dt(dt_source="local_file", local_dt_filepath=dtb, arch="arm") config = { - 'PLL0': { - 'hitless': { - 'fb_source': 0, - 'fb_source_rate': 10000000 - }, - 'n0_profile_0': 1228800000.0, - 'n0_profile_2': 6144.0, - 'rate_hz': 1228800000.0, - 'priority_source_0': 5, - 'priority_source_2': 15, - 'priority_source_4': 25, - }, - 'q0': 40.0, - 'r0': 1.0, - 'r2': 50.0, + "PLL0": { + "hitless": {"fb_source": 0, "fb_source_rate": 10000000}, + "n0_profile_0": 1228800000.0, + "n0_profile_2": 6144.0, + "rate_hz": 1228800000.0, + "priority_source_0": 5, + "priority_source_2": 15, + "priority_source_4": 25, + }, + "q0": 40.0, + "r0": 1.0, + "r2": 50.0, } node = d.get_node_by_compatible("adi,ad9545") @@ -44,9 +42,7 @@ def test_ad9545_add_nodes(): if r_div != 0: ref_node = node.get_subnode("ref-input-clk@" + str(i)) if ref_node is None: - raise Exception( - "AD9545: missing node: ref-input-clk@" + str(i) - ) + raise Exception("AD9545: missing node: ref-input-clk@" + str(i)) dt_r_div = ref_node.get_property("adi,r-divider-ratio").value assert int(dt_r_div) == int(r_div) @@ -95,9 +91,7 @@ def test_ad9545_add_nodes(): if pll_profile_node is None: continue - adi_pll_source_nr = list( - pll_profile_node.get_property("adi,pll-source") - )[0] + adi_pll_source_nr = list(pll_profile_node.get_property("adi,pll-source"))[0] priority_attr = "priority_source_" + str(adi_pll_source_nr) if (priority_attr) in config[pll_name]: @@ -127,17 +121,11 @@ def test_ad9545_add_nodes(): fb_source_nr = hitless_dict["fb_source"] fb_source_rate = hitless_dict["fb_source_rate"] - dt_fb_source_nr = list( - pll_node.get_property(dt_fb_source_nr_str) - )[0] + dt_fb_source_nr = list(pll_node.get_property(dt_fb_source_nr_str))[0] - dt_fb_source_rate = list( - pll_node.get_property(dt_fb_source_rate_str) - )[0] + dt_fb_source_rate = list(pll_node.get_property(dt_fb_source_rate_str))[0] - dt_slew_rate = list( - pll_node.get_property(dt_slew_rate_str) - )[0] + dt_slew_rate = list(pll_node.get_property(dt_slew_rate_str))[0] assert int(fb_source_nr) == int(dt_fb_source_nr) assert int(fb_source_rate) == int(dt_fb_source_rate) @@ -152,9 +140,7 @@ def test_ad9545_add_nodes(): raise Exception("AD9545: invalid mode: pll-clk@" + str(i)) # slew rate should be returned to default value - dt_slew_rate = list( - pll_node.get_property(dt_slew_rate_str) - )[0] + dt_slew_rate = list(pll_node.get_property(dt_slew_rate_str))[0] assert 100000000 == int(dt_slew_rate) diff --git a/test/adrv9009/Tx_BW200_IR245p76_Rx_BW200_OR245p76_ORx_BW200_OR245p76_DC245p76.txt b/test/adrv9009/Tx_BW200_IR245p76_Rx_BW200_OR245p76_ORx_BW200_OR245p76_DC245p76.txt new file mode 100644 index 00000000..5d3ed8f9 --- /dev/null +++ b/test/adrv9009/Tx_BW200_IR245p76_Rx_BW200_OR245p76_ORx_BW200_OR245p76_DC245p76.txt @@ -0,0 +1,418 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -7 + -23 + 33 + 50 + -70 + -110 + 144 + 205 + -259 + -356 + 437 + 581 + -698 + -916 + 1082 + 1415 + -1655 + -2209 + 2567 + 3615 + -4351 + -7169 + 9329 + 31129 + 31129 + 9329 + -7169 + -4351 + 3615 + 2567 + -2209 + -1655 + 1415 + 1082 + -916 + -698 + 581 + 437 + -356 + -259 + 205 + 144 + -110 + -70 + 50 + 33 + -23 + -7 + + + + 185 + 141 + 172 + 90 + 1280 + 942 + 1332 + 90 + 1368 + 46 + 1016 + 19 + 48 + 48 + 37 + 208 + 0 + 0 + 0 + 0 + 52 + 0 + 7 + 6 + 42 + 0 + 7 + 6 + 42 + 0 + 25 + 27 + 0 + 0 + 25 + 27 + 0 + 0 + 165 + 44 + 31 + 905 + + + + + + + + + + + + + + + -7 + -21 + 31 + 48 + -67 + -106 + 124 + 164 + -275 + -334 + 440 + 552 + -694 + -872 + 1069 + 1351 + -1633 + -2111 + 2541 + 3477 + -4295 + -6877 + 9433 + 30825 + 30825 + 9433 + -6877 + -4295 + 3477 + 2541 + -2111 + -1633 + 1351 + 1069 + -872 + -694 + 552 + 440 + -334 + -275 + 164 + 124 + -106 + -67 + 48 + 31 + -21 + -7 + + + + 185 + 141 + 172 + 90 + 1280 + 942 + 1332 + 90 + 1368 + 46 + 1016 + 19 + 48 + 48 + 37 + 208 + 0 + 0 + 0 + 0 + 52 + 0 + 7 + 6 + 42 + 0 + 7 + 6 + 42 + 0 + 25 + 27 + 0 + 0 + 25 + 27 + 0 + 0 + 165 + 44 + 31 + 905 + + + + 185 + 141 + 172 + 90 + 1280 + 942 + 1332 + 90 + 1368 + 46 + 1016 + 19 + 48 + 48 + 37 + 208 + 0 + 0 + 0 + 0 + 52 + 0 + 7 + 6 + 42 + 0 + 7 + 6 + 42 + 0 + 25 + 27 + 0 + 0 + 25 + 27 + 0 + 0 + 165 + 44 + 31 + 905 + + + + + + + + + + + + + -7 + -21 + 31 + 48 + -67 + -106 + 124 + 164 + -275 + -334 + 440 + 552 + -694 + -872 + 1069 + 1351 + -1633 + -2111 + 2541 + 3477 + -4295 + -6877 + 9433 + 30825 + 30825 + 9433 + -6877 + -4295 + 3477 + 2541 + -2111 + -1633 + 1351 + 1069 + -872 + -694 + 552 + 440 + -334 + -275 + 164 + 124 + -106 + -67 + 48 + 31 + -21 + -7 + + + + 243 + 143 + 181 + 90 + 1280 + 485 + 1275 + 37 + 1317 + 23 + 797 + 35 + 48 + 48 + 30 + 174 + 0 + 0 + 0 + 0 + 44 + 0 + 7 + 6 + 42 + 0 + 7 + 6 + 42 + 0 + 25 + 27 + 0 + 0 + 25 + 27 + 0 + 0 + 165 + 44 + 31 + 905 + + + + + + + + + + + + + + + + + + 33 + -77 + 123 + -158 + 171 + -112 + -155 + 1040 + -3011 + 20121 + -3011 + 1040 + -155 + -112 + 171 + -158 + 123 + -77 + 33 + 0 + + + diff --git a/test/adrv9009/adrv9009_ad9528.dtb b/test/adrv9009/adrv9009_ad9528.dtb new file mode 100755 index 00000000..26d54f3f Binary files /dev/null and b/test/adrv9009/adrv9009_ad9528.dtb differ diff --git a/test/adrv9009/test_adrv9009_zu11eg.py b/test/adrv9009/test_adrv9009_zu11eg.py new file mode 100644 index 00000000..f6579364 --- /dev/null +++ b/test/adrv9009/test_adrv9009_zu11eg.py @@ -0,0 +1,36 @@ +import os +from pprint import pprint + +import adidt as dt +import pytest + + +def test_import_profile(kernel_build_config): + + # Map to DT + # loc = os.path.dirname(__file__) + # dtb = os.path.join(loc, "adrv9009_ad9528.dtb") + + # d = dt.adrv9009_dt(dt_source="local_file", local_dt_filepath=dtb, arch="arm64") + + loc = os.path.dirname(__file__) + profile = os.path.join( + loc, "Tx_BW200_IR245p76_Rx_BW200_OR245p76_ORx_BW200_OR245p76_DC245p76.txt" + ) + # dprofile = d.parse_profile(profile) + + # Generate DT fragment + som = dt.adrv9009_zu11eg() + som.parse_profile(profile) + # clock, adc, dac, fpga = som.map_clocks_to_board_layout(cfg) + dts_filename = som.gen_dt() + print(f"Generated DTS file: {dts_filename}") + + kernel_build_config["devicetree_to_test"] = os.path.join(os.getcwd(), dts_filename) + + +def test_kernel_build(kernel_build_config): + kernel_build_config["branch"] = "master" + loc = os.path.dirname(__file__) + dts = os.path.join(loc, "adrv9009_zu11eg_out.dts") + kernel_build_config["devicetree_to_test"] = dts diff --git a/test/conftest.py b/test/conftest.py index 4d18ee26..f3257ecc 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,12 +1,68 @@ +import os + import pytest + def pytest_addoption(parser): parser.addoption("--ip", action="store", default=None) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def ip(request): ip_value = request.config.option.ip if ip_value is None: pytest.skip("Test requires an IP address set") - return ip_value \ No newline at end of file + return ip_value + + +@pytest.fixture() +def kernel_build_config(request): + config = { + "branch": "master", + "arch": "arm64", + "repo_dir": "linux", + "devicetree_to_test": "", + } + yield config + + if not os.path.isfile(config["devicetree_to_test"]): + raise Exception(f"Device tree {config['devicetree_to_test']} not found") + target = os.path.abspath(config["devicetree_to_test"]) + + if config["arch"] == "arm64": + compiler = "aarch64-linux-gnu-" + defconfig = "adi_zynqmp_defconfig" + elif config["arch"] == "arm32": + compiler = "arm-linux-gnueabihf-" + defconfig = "zynq_xcomm_adv7511_defconfig" + else: + raise ValueError(f"Unknown arch {config['arch']}") + target_copy = f"arch/{config['arch']}/boot/dts/xilinx/" + + if not os.path.isdir(config["repo_dir"]): + os.system( + f'git clone https://github.com/analogdevicesinc/linux.git --depth=1 -b {config["branch"]} {config["repo_dir"]}' + ) + # Build the kernel + os.chdir(config["repo_dir"]) + + cmd = f'make CROSS_COMPILE={compiler} ARCH={config["arch"]} {defconfig}' + print(f"Running: {cmd}") + os.system(cmd) + cmd = f'make CROSS_COMPILE={compiler} ARCH={config["arch"]}' + print(f"Running: {cmd}") + os.system(cmd) + + # Copy in generated devicetree + e = os.system(f"cp {target} {target_copy}") + if e != 0: + raise Exception(f"Failed to copy {target} to {target_copy}") + + dts_filename = os.path.basename(target) + + # Build the device tree + cmd = f"make CROSS_COMPILE={compiler} ARCH={config['arch']} xilinx/{dts_filename.replace('.dts', '.dtb')}" + print(f"Running: {cmd}") + e = os.system(cmd) + if e != 0: + raise Exception(f"Failed to build device tree {dts_filename}") diff --git a/test/hmc7044/test_hmc7044.py b/test/hmc7044/test_hmc7044.py index 583ec2c4..d986fe8c 100644 --- a/test/hmc7044/test_hmc7044.py +++ b/test/hmc7044/test_hmc7044.py @@ -1,7 +1,7 @@ -import pytest import os import adidt as dt +import pytest def test_hmc7044_add_nodes(): @@ -12,22 +12,35 @@ def test_hmc7044_add_nodes(): clock = { "vco": 125000000 * 2, - "reference_frequencies" : [38400000, 38400000, 38400000, 38400000], - "reference_selection_order" : [0, 3, 2, 1], + "reference_frequencies": [38400000, 38400000, 38400000, 38400000], + "reference_selection_order": [0, 3, 2, 1], "n2": 24, "out_dividers": [3, 6, 384], "output_clocks": { - "ADC": {"divider": 3, "rate": 1000000000.0, "driver-mode": "CML", + "ADC": { + "divider": 3, + "rate": 1000000000.0, + "driver-mode": "CML", "high-performance-mode-disable": True, "startup-mode-dynamic-enable": True, "dynamic-driver-enable": True, "force-mute-enable": True, "output-mux-mode": "CH_DIV", - "driver_impedances": "100_OHM"}, - "FPGA": {"divider": 6, "rate": 500000000.0, "driver-mode": "CML", - "fine-delay": 16, "coarse-delay": 5}, - "SYSREF": {"divider": 384, "rate": 7812500.0, "driver-mode": "CMOS", - "CMOS": {"P" : 1, "N" : 0}}, + "driver_impedances": "100_OHM", + }, + "FPGA": { + "divider": 6, + "rate": 500000000.0, + "driver-mode": "CML", + "fine-delay": 16, + "coarse-delay": 5, + }, + "SYSREF": { + "divider": 384, + "rate": 7812500.0, + "driver-mode": "CMOS", + "CMOS": {"P": 1, "N": 0}, + }, }, "r2": 2, } @@ -43,14 +56,14 @@ def test_hmc7044_add_nodes(): assert node.get_property("adi,vcxo-frequency").value == config["vcxo"] # Check input reference priorities - if ("reference_selection_order" in clock): + if "reference_selection_order" in clock: ref_order = [] priority = 0 ref_order_prop_val = node.get_property("adi,pll1-ref-prio-ctrl").value # MSB (Fourth priority input [1:0]) .... (First priority input [1:0]) LSB for ref_nr in clock["reference_selection_order"]: - if (ref_nr > 4): + if ref_nr > 4: raise Exception("Refernce number:" + str(ref_nr) + " invalid.") ref_order.append((ref_order_prop_val >> (priority * 2)) & 0x3) priority += 1 @@ -79,7 +92,7 @@ def test_hmc7044_add_nodes(): assert clock["reference_frequencies"][i] == clock_freq.value i += 1 - divs = [clock['output_clocks'][oc]['divider'] for oc in clock['output_clocks']] + divs = [clock["output_clocks"][oc]["divider"] for oc in clock["output_clocks"]] for n in node.nodes: assert n.get_property("adi,extended-name").value in list( clock["output_clocks"].keys() @@ -117,13 +130,15 @@ def test_hmc7044_add_nodes(): prop = output_node.get_property("adi,force-mute-enable") assert prop != None - if ("output-mux-mode" in output_dict): + if "output-mux-mode" in output_dict: mux_mode = d.output_mux_modes[output_dict["output-mux-mode"]] prop = output_node.get_property("adi,output-mux-mode") assert prop.value == mux_mode - if ("driver-impedance-mode" in output_dict): - impedance_mode = self.driver_impedances[output_dict["driver-impedance-mode"]] + if "driver-impedance-mode" in output_dict: + impedance_mode = self.driver_impedances[ + output_dict["driver-impedance-mode"] + ] prop = output_node.get_property("adi,driver-impedance-mode") assert prop.value == impedance_mode @@ -136,9 +151,17 @@ def test_hmc7044_add_nodes(): assert fine_delay == output_dict["coarse-delay"] if "CMOS" in output_dict: - impedance_prop_val = output_node.get_property("adi,driver-impedance-mode").value - val_p = (output_dict["CMOS"]["P"] << dt.hmc7044_dt.cmos_outputs_reg_field_map[i]["P"]) - val_n = (output_dict["CMOS"]["N"] << dt.hmc7044_dt.cmos_outputs_reg_field_map[i]["N"]) + impedance_prop_val = output_node.get_property( + "adi,driver-impedance-mode" + ).value + val_p = ( + output_dict["CMOS"]["P"] + << dt.hmc7044_dt.cmos_outputs_reg_field_map[i]["P"] + ) + val_n = ( + output_dict["CMOS"]["N"] + << dt.hmc7044_dt.cmos_outputs_reg_field_map[i]["N"] + ) impedance_prop_dict = val_p | val_n assert impedance_prop_val == impedance_prop_dict diff --git a/test/test_dt.py b/test/test_dt.py index 758f74ea..8b682f6e 100644 --- a/test/test_dt.py +++ b/test/test_dt.py @@ -1,7 +1,7 @@ -import pytest import os import adidt +import pytest loc = os.path.dirname(__file__)