From c6807f4594c189467b74379fb8a225cb1ce644ac Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 29 Dec 2020 19:18:12 +0100 Subject: [PATCH] kasli_generic: validate description against schema, use defaults from schema --- MANIFEST.in | 1 + artiq/frontend/artiq_ddb_template.py | 33 +++++++++-------- artiq/gateware/jsondesc.py | 35 +++++++++++++++++++ artiq/gateware/targets/kasli_generic.py | 24 ++++++------- .../targets/kasli_generic.schema.json | 6 ++-- 5 files changed, 66 insertions(+), 33 deletions(-) create mode 100644 artiq/gateware/jsondesc.py diff --git a/MANIFEST.in b/MANIFEST.in index 159e4d9ed..9182037f6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,3 +3,4 @@ graft artiq/examples include artiq/gui/logo*.svg include versioneer.py include artiq/_version.py +include artiq/gateware/targets/kasli_generic.schema.json diff --git a/artiq/frontend/artiq_ddb_template.py b/artiq/frontend/artiq_ddb_template.py index 15d584fb1..718d50511 100755 --- a/artiq/frontend/artiq_ddb_template.py +++ b/artiq/frontend/artiq_ddb_template.py @@ -2,12 +2,12 @@ import argparse import sys -import json import textwrap from collections import defaultdict from itertools import count from artiq import __version__ as artiq_version +from artiq.gateware import jsondesc def process_header(output, description): @@ -57,8 +57,8 @@ def process_header(output, description): }} """).format( variant=description["variant"], - core_addr=description.get("core_addr", "192.168.1.70"), - ref_period=1/(8*description.get("rtio_frequency", 125e6))), + core_addr=description["core_addr"], + ref_period=1/(8*description["rtio_frequency"])), file=output) @@ -116,7 +116,7 @@ class PeripheralManager: def process_urukul(self, rtio_offset, peripheral): urukul_name = self.get_name("urukul") - synchronization = peripheral.get("synchronization", False) + synchronization = peripheral["synchronization"] channel = count(0) self.gen(""" device_db["eeprom_{name}"]={{ @@ -181,10 +181,10 @@ class PeripheralManager: }}""", name=urukul_name, sync_device="\"ttl_{name}_sync\"".format(name=urukul_name) if synchronization else "None", - refclk=peripheral.get("refclk", self.master_description.get("rtio_frequency", 125e6)), + refclk=peripheral.get("refclk", self.master_description["rtio_frequency"]), clk_sel=peripheral["clk_sel"]) - dds = peripheral.get("dds", "ad9910") - pll_vco = peripheral.get("pll_vco", None) + dds = peripheral["dds"] + pll_vco = peripheral.get("pll_vco") for i in range(4): if dds == "ad9910": self.gen(""" @@ -280,8 +280,8 @@ class PeripheralManager: }}, }}""", name=mirny_name, - refclk=peripheral.get("refclk", 100e6), - clk_sel=peripheral.get("clk_sel", 0)) + refclk=peripheral["refclk"], + clk_sel=peripheral["clk_sel"]) return next(channel) @@ -394,7 +394,7 @@ class PeripheralManager: }}""", sampler_name=sampler_name, sampler_channel=rtio_offset+next(channel)) - pll_vco = peripheral.get("pll_vco", None) + pll_vco = peripheral.get("pll_vco") for urukul_name in (urukul0_name, urukul1_name): self.gen(""" device_db["spi_{urukul_name}"] = {{ @@ -425,10 +425,10 @@ class PeripheralManager: }}""", urukul_name=urukul_name, urukul_channel=rtio_offset+next(channel), - refclk=peripheral.get("refclk", self.master_description.get("rtio_frequency", 125e6)), + refclk=peripheral.get("refclk", self.master_description["rtio_frequency"]), clk_sel=peripheral["clk_sel"], pll_vco=",\n \"pll_vco\": {}".format(pll_vco) if pll_vco is not None else "", - pll_n=peripheral.get("pll_n", 32)) + pll_n=peripheral["pll_n"]) return next(channel) def process_zotino(self, rtio_offset, peripheral): @@ -573,13 +573,12 @@ def main(): args = parser.parse_args() - with open(args.master_description, "r") as f: - master_description = json.load(f) + master_description = jsondesc.load(args.master_description) satellites = [] - for destination, description in args.satellite: - with open(description, "r") as f: - satellites.append((int(destination, 0), json.load(f))) + for destination, description_path in args.satellite: + satellite_description = jsondesc.load(description_path) + satellites.append((int(destination, 0), satellite_description)) if args.output is not None: with open(args.output, "w") as f: diff --git a/artiq/gateware/jsondesc.py b/artiq/gateware/jsondesc.py new file mode 100644 index 000000000..b936c3a43 --- /dev/null +++ b/artiq/gateware/jsondesc.py @@ -0,0 +1,35 @@ +from os import path +import json +from jsonschema import Draft7Validator, validators + +def extend_with_default(validator_class): + validate_properties = validator_class.VALIDATORS["properties"] + + def set_defaults(validator, properties, instance, schema): + for property, subschema in properties.items(): + if "default" in subschema: + instance.setdefault(property, subschema["default"]) + + for error in validate_properties( + validator, properties, instance, schema, + ): + yield error + + return validators.extend( + validator_class, {"properties" : set_defaults}, + ) + +schema_path = path.join(path.dirname(__file__), "targets/kasli_generic.schema.json") +with open(schema_path, "r") as f: + schema = json.load(f) + +validator = extend_with_default(Draft7Validator)(schema) + +def load(description_path): + with open(description_path, "r") as f: + result = json.load(f) + + global validator + validator.validate(result) + + return result diff --git a/artiq/gateware/targets/kasli_generic.py b/artiq/gateware/targets/kasli_generic.py index 577b93dad..168b14ee4 100755 --- a/artiq/gateware/targets/kasli_generic.py +++ b/artiq/gateware/targets/kasli_generic.py @@ -1,14 +1,12 @@ #!/usr/bin/env python3 import argparse -import json from misoc.integration.builder import builder_args, builder_argdict from misoc.targets.kasli import soc_kasli_args, soc_kasli_argdict -from artiq.gateware import rtio +from artiq.gateware import rtio, eem, jsondesc from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, edge_counter -from artiq.gateware import eem from artiq.gateware.targets.kasli import StandaloneBase, MasterBase, SatelliteBase from artiq.build_soc import * @@ -20,7 +18,7 @@ def peripheral_dio(module, peripheral): } if len(peripheral["ports"]) != 1: raise ValueError("wrong number of ports") - if peripheral.get("edge_counter", False): + if peripheral["edge_counter"]: edge_counter_cls = edge_counter.SimpleEdgeCounter else: edge_counter_cls = None @@ -37,7 +35,7 @@ def peripheral_urukul(module, peripheral): port, port_aux = peripheral["ports"] else: raise ValueError("wrong number of ports") - if peripheral.get("synchronization", False): + if peripheral["synchronization"]: sync_gen_cls = ttl_simple.ClockGen else: sync_gen_cls = None @@ -110,7 +108,7 @@ def peripheral_fastino(module, peripheral): if len(peripheral["ports"]) != 1: raise ValueError("wrong number of ports") eem.Fastino.add_std(module, peripheral["ports"][0], - peripheral.get("log2_width", 0)) + peripheral["log2_width"]) def peripheral_phaser(module, peripheral): @@ -146,7 +144,7 @@ class GenericStandalone(StandaloneBase): StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs) self.config["SI5324_AS_SYNTHESIZER"] = None - self.config["RTIO_FREQUENCY"] = "{:.1f}".format(description.get("rtio_frequency", 125e6)/1e6) + self.config["RTIO_FREQUENCY"] = "{:.1f}".format(description["rtio_frequency"]/1e6) if "ext_ref_frequency" in description: self.config["SI5324_EXT_REF"] = None self.config["EXT_REF_FREQUENCY"] = "{:.1f}".format( @@ -189,8 +187,8 @@ class GenericMaster(MasterBase): self.class_name_override = description["variant"] MasterBase.__init__(self, hw_rev=hw_rev, - rtio_clk_freq=description.get("rtio_frequency", 125e6), - enable_sata=description.get("enable_sata_drtio", False), + rtio_clk_freq=description["rtio_frequency"], + enable_sata=description["enable_sata_drtio"], **kwargs) if "ext_ref_frequency" in description: self.config["SI5324_EXT_REF"] = None @@ -226,8 +224,8 @@ class GenericSatellite(SatelliteBase): self.class_name_override = description["variant"] SatelliteBase.__init__(self, hw_rev=hw_rev, - rtio_clk_freq=description.get("rtio_frequency", 125e6), - enable_sata=description.get("enable_sata_drtio", False), + rtio_clk_freq=description["rtio_frequency"], + enable_sata=description["enable_sata_drtio"], **kwargs) if hw_rev == "v1.0": # EEM clock fan-out from Si5324, not MMCX @@ -263,9 +261,7 @@ def main(): parser.add_argument("--gateware-identifier-str", default=None, help="Override ROM identifier") args = parser.parse_args() - - with open(args.description, "r") as f: - description = json.load(f) + description = jsondesc.load(args.description) if description["target"] != "kasli": raise ValueError("Description is for a different target") diff --git a/artiq/gateware/targets/kasli_generic.schema.json b/artiq/gateware/targets/kasli_generic.schema.json index 0af0441dd..1ea8a6d09 100644 --- a/artiq/gateware/targets/kasli_generic.schema.json +++ b/artiq/gateware/targets/kasli_generic.schema.json @@ -42,7 +42,8 @@ "core_addr": { "type": "string", "format": "ipv4", - "description": "IPv4 address" + "description": "IPv4 address", + "default": "192.168.1.70" }, "vendor": { "type": "string", @@ -262,7 +263,8 @@ "maximum": 3 }, "pll_n": { - "type": "integer" + "type": "integer", + "default": 32 }, "pll_vco": { "type": "integer"