|  | import json | 
|  |  | 
|  | from pyprd.chip_data import chip_data as cd | 
|  |  | 
|  | # Encoder support -------------------------------------------------------------- | 
|  | # This extends the json.JSONEncoder class so that we can use a lot of the | 
|  | # built-in json support. | 
|  |  | 
|  |  | 
|  | def _get_addr_str(reg_type: str, address: int) -> str: | 
|  | fmt = { | 
|  | "SCOM": "0x{:08X}",  # 4-byte address | 
|  | "IDSCOM": "0x{:016X}",  # 8-byte address | 
|  | } | 
|  |  | 
|  | return fmt[reg_type].format(address)  # throws exception if key not found | 
|  |  | 
|  |  | 
|  | def _get_value_str(reg_type: str, value: int) -> str: | 
|  | fmt = { | 
|  | "SCOM": "0x{:016X}",  # 8-byte value | 
|  | "IDSCOM": "0x{:016X}",  # 8-byte value | 
|  | } | 
|  |  | 
|  | return fmt[reg_type].format(value) | 
|  |  | 
|  |  | 
|  | class _ChipDataEncoder(json.JSONEncoder): | 
|  | def default(self, o: object) -> dict: | 
|  | if isinstance(o, cd.Base): | 
|  | j = { | 
|  | "version": o.version, | 
|  | "model_ec": o.model_ec, | 
|  | } | 
|  |  | 
|  | if o.registers: | 
|  | j["registers"] = o.registers | 
|  |  | 
|  | if o.isolation_nodes: | 
|  | j["isolation_nodes"] = o.isolation_nodes | 
|  |  | 
|  | if o.root_nodes: | 
|  | j["root_nodes"] = o.root_nodes | 
|  |  | 
|  | if o.capture_groups: | 
|  | j["capture_groups"] = o.capture_groups | 
|  |  | 
|  | return j | 
|  |  | 
|  | if isinstance(o, cd.Register): | 
|  | j = {} | 
|  |  | 
|  | if "SCOM" != o.reg_type:  # Don't add default to save space. | 
|  | j["reg_type"] = o.reg_type | 
|  |  | 
|  | if "RW" != o.access:  # Don't add default to save space. | 
|  | j["access"] = o.access | 
|  |  | 
|  | j["instances"] = dict( | 
|  | zip( | 
|  | o.instances.keys(), | 
|  | map( | 
|  | lambda v: _get_addr_str(o.reg_type, v), | 
|  | o.instances.values(), | 
|  | ), | 
|  | ) | 
|  | ) | 
|  |  | 
|  | return j | 
|  |  | 
|  | if isinstance(o, cd.IsolationNode): | 
|  | j = {} | 
|  |  | 
|  | if "SCOM" != o.reg_type:  # Don't add default to save space. | 
|  | j["reg_type"] = o.reg_type | 
|  |  | 
|  | j["instances"] = o.instances | 
|  | j["rules"] = o.rules | 
|  | j["bits"] = o.bits | 
|  |  | 
|  | if o.capture_groups: | 
|  | j["capture_groups"] = o.capture_groups | 
|  |  | 
|  | return j | 
|  |  | 
|  | if isinstance(o, cd.IsolationRule): | 
|  | return { | 
|  | "attn_type": o.attn_type, | 
|  | "node_inst": o.node_inst, | 
|  | "expr": o.expr, | 
|  | } | 
|  |  | 
|  | if isinstance(o, cd.ExprReg): | 
|  | j = { | 
|  | "expr_type": o.expr_type, | 
|  | "reg_name": o.reg_name, | 
|  | } | 
|  |  | 
|  | if o.reg_inst: | 
|  | j["reg_inst"]: o.reg_inst | 
|  |  | 
|  | return j | 
|  |  | 
|  | if isinstance(o, cd.ExprInt): | 
|  | return { | 
|  | "expr_type": o.expr_type, | 
|  | "int_value": _get_value_str(o.reg_type, o.int_value), | 
|  | } | 
|  |  | 
|  | if isinstance(o, cd.ExprAnd): | 
|  | return { | 
|  | "expr_type": o.expr_type, | 
|  | "exprs": o.exprs, | 
|  | } | 
|  |  | 
|  | if isinstance(o, cd.ExprOr): | 
|  | return { | 
|  | "expr_type": o.expr_type, | 
|  | "exprs": o.exprs, | 
|  | } | 
|  |  | 
|  | if isinstance(o, cd.ExprNot): | 
|  | return { | 
|  | "expr_type": o.expr_type, | 
|  | "expr": o.expr, | 
|  | } | 
|  |  | 
|  | if isinstance(o, cd.ExprLeftShift): | 
|  | return { | 
|  | "expr_type": o.expr_type, | 
|  | "expr": o.expr, | 
|  | "shift_value": o.shift_value, | 
|  | } | 
|  |  | 
|  | if isinstance(o, cd.ExprRightShift): | 
|  | return { | 
|  | "expr_type": o.expr_type, | 
|  | "expr": o.expr, | 
|  | "shift_value": o.shift_value, | 
|  | } | 
|  |  | 
|  | if isinstance(o, cd.IsolationBit): | 
|  | j = {"desc": o.desc} | 
|  |  | 
|  | if o.child_node: | 
|  | j["child_node"] = {"name": o.child_node["name"]} | 
|  |  | 
|  | if o.child_node["inst"]: | 
|  | j["child_node"]["inst"] = o.child_node["inst"] | 
|  |  | 
|  | if o.capture_groups: | 
|  | j["capture_groups"] = o.capture_groups | 
|  |  | 
|  | return j | 
|  |  | 
|  | if isinstance(o, cd.RootNode): | 
|  | return { | 
|  | "name": o.name, | 
|  | "inst": o.inst, | 
|  | } | 
|  |  | 
|  | if isinstance(o, cd.CaptureGroup): | 
|  | return { | 
|  | "group_name": o.group_name, | 
|  | "group_inst": o.group_inst, | 
|  | } | 
|  |  | 
|  | if isinstance(o, cd.CaptureRegister): | 
|  | return { | 
|  | "reg_name": o.reg_name, | 
|  | "reg_inst": o.reg_inst, | 
|  | } | 
|  |  | 
|  | # Call the default method for other types | 
|  | return json.JSONEncoder.default(self, o) | 
|  |  | 
|  |  | 
|  | # Decoder support -------------------------------------------------------------- | 
|  | # Unfortunately, we cannot extent the json.JSONDecoder like we did with the | 
|  | # encoder because when using a custom encoder it is applied to all JSON objects | 
|  | # in the string/file. Since the Chip Data JSON has a complex design with many | 
|  | # different JSON object formats, we can't use this approach without adding a | 
|  | # property to each object indicating what type of object it is. That would end | 
|  | # up bloating the JSON and is not desired. Instead, we'll create a series of | 
|  | # decoder functions. Then the decoder helper at the bottom of the file will use | 
|  | # the default decoder and apply these decoder functions on the returned | 
|  | # dictionary. This is not as efficient, but will be the approach for now. | 
|  |  | 
|  |  | 
|  | def _decodeInstanceMap(d: dict) -> dict: | 
|  | # Need to convert the keys to integers. | 
|  | return dict(zip(map(lambda k: int(k), d.keys()), d.values())) | 
|  |  | 
|  |  | 
|  | def _decodeRegister(d: dict) -> cd.Register: | 
|  | reg_type = d["reg_type"] if "reg_type" in d else "SCOM" | 
|  | access = d["access"] if "access" in d else "RW" | 
|  |  | 
|  | register = cd.Register(reg_type, access) | 
|  |  | 
|  | for k, v in d["instances"].items(): | 
|  | register.addInstance(int(k), int(v, 16)) | 
|  |  | 
|  | return register | 
|  |  | 
|  |  | 
|  | def _decodeRuleExpression(reg_type: str, d: dict) -> object: | 
|  | expr_type = d["expr_type"] | 
|  |  | 
|  | if "reg" == expr_type: | 
|  | reg_inst = _decodeInstanceMap(d["reg_inst"]) if "reg_inst" in d else {} | 
|  | return cd.ExprReg(d["reg_name"], reg_inst) | 
|  |  | 
|  | if "int" == expr_type: | 
|  | return cd.ExprInt(reg_type, int(d["int_value"], 16)) | 
|  |  | 
|  | if "and" == expr_type: | 
|  | return cd.ExprAnd( | 
|  | list(map(lambda e: _decodeRuleExpression(reg_type, e), d["exprs"])) | 
|  | ) | 
|  |  | 
|  | if "or" == expr_type: | 
|  | return cd.ExprOr( | 
|  | list(map(lambda e: _decodeRuleExpression(reg_type, e), d["exprs"])) | 
|  | ) | 
|  |  | 
|  | if "not" == expr_type: | 
|  | return cd.ExprNot(_decodeRuleExpression(reg_type, d["expr"])) | 
|  |  | 
|  | if "lshift" == expr_type: | 
|  | return cd.ExprLeftShift( | 
|  | _decodeRuleExpression(reg_type, d["expr"]), d["shift_value"] | 
|  | ) | 
|  |  | 
|  | if "rshift" == expr_type: | 
|  | return cd.ExprRightShift( | 
|  | _decodeRuleExpression(reg_type, d["expr"]), d["shift_value"] | 
|  | ) | 
|  |  | 
|  |  | 
|  | def _decodeIsolationRule(reg_type: str, d: dict) -> cd.IsolationRule: | 
|  | return cd.IsolationRule( | 
|  | d["attn_type"], | 
|  | d["node_inst"], | 
|  | _decodeRuleExpression(reg_type, d["expr"]), | 
|  | ) | 
|  |  | 
|  |  | 
|  | def _decodeCaptureGroup(d: dict) -> cd.CaptureGroup: | 
|  | return cd.CaptureGroup( | 
|  | d["group_name"], _decodeInstanceMap(d["group_inst"]) | 
|  | ) | 
|  |  | 
|  |  | 
|  | def _decodeIsolationBit(d: dict) -> cd.IsolationBit: | 
|  | bit = cd.IsolationBit(d["desc"]) | 
|  |  | 
|  | if "child_node" in d: | 
|  | if "inst" in d["child_node"]: | 
|  | inst = _decodeInstanceMap(d["child_node"]["inst"]) | 
|  | else: | 
|  | inst = {} | 
|  |  | 
|  | bit.addChildNode(d["child_node"]["name"], inst) | 
|  |  | 
|  | if "capture_groups" in d: | 
|  | for e in d["capture_groups"]: | 
|  | bit.addCaptureGroup(_decodeCaptureGroup(e)) | 
|  |  | 
|  | return bit | 
|  |  | 
|  |  | 
|  | def _decodeIsolationNode(d: dict) -> cd.IsolationNode: | 
|  | reg_type = d["reg_type"] if "reg_type" in d else "SCOM" | 
|  |  | 
|  | node = cd.IsolationNode(reg_type) | 
|  |  | 
|  | # Don't need to parse `instances` because that will be recreated in the | 
|  | # `addRule()` function. | 
|  |  | 
|  | for e in d["rules"]: | 
|  | node.addRule(_decodeIsolationRule(reg_type, e)) | 
|  |  | 
|  | for k, v in d["bits"].items(): | 
|  | node.addBit(k, _decodeIsolationBit(v)) | 
|  |  | 
|  | if "capture_groups" in d: | 
|  | for e in d["capture_groups"]: | 
|  | node.addCaptureGroup(_decodeCaptureGroup(e)) | 
|  |  | 
|  | return node | 
|  |  | 
|  |  | 
|  | def _decodeRootNode(d: dict) -> cd.RootNode: | 
|  | return cd.RootNode(d["name"], d["inst"]) | 
|  |  | 
|  |  | 
|  | def _decodeCaptureRegister(d: dict) -> cd.CaptureRegister: | 
|  | return cd.CaptureRegister(d["reg_name"], _decodeInstanceMap(d["reg_inst"])) | 
|  |  | 
|  |  | 
|  | def _decodeBase(d: dict) -> cd.Base: | 
|  | base = cd.Base(d["version"], d["model_ec"]) | 
|  |  | 
|  | if "registers" in d: | 
|  | for k, v in d["registers"].items(): | 
|  | base.addRegister(k, _decodeRegister(v)) | 
|  |  | 
|  | if "isolation_nodes" in d: | 
|  | for k, v in d["isolation_nodes"].items(): | 
|  | base.addIsolationNode(k, _decodeIsolationNode(v)) | 
|  |  | 
|  | if "root_nodes" in d: | 
|  | for k, v in d["root_nodes"].items(): | 
|  | base.addRootNode(k, _decodeRootNode(v)) | 
|  |  | 
|  | if "capture_groups" in d: | 
|  | for k, v in d["capture_groups"].items(): | 
|  | for e in v: | 
|  | base.addCaptureRegister(k, _decodeCaptureRegister(e)) | 
|  |  | 
|  | return base | 
|  |  | 
|  |  | 
|  | # ------------------------------------------------------------------------------ | 
|  |  | 
|  |  | 
|  | def json_encode(obj: object, fp: object, indent=None): | 
|  | json.dump(obj, fp, cls=_ChipDataEncoder, indent=indent) | 
|  | fp.write("\n") | 
|  |  | 
|  |  | 
|  | def json_encodes(obj: object, indent=None) -> str: | 
|  | return json.dump(obj, cls=_ChipDataEncoder, indent=indent) | 
|  |  | 
|  |  | 
|  | def json_decode(fp: object) -> object: | 
|  | # See note above for why we didn't extend the json.JSONDecoder class | 
|  | return _decodeBase(json.load(fp)) | 
|  |  | 
|  |  | 
|  | def json_decodes(s: str) -> object: | 
|  | # See note above for why we didn't extend the json.JSONDecoder class | 
|  | return _decodeBase(json.loads(s)) |