blob: 67a3e060f43a82ce5c46c644c0b2092dc1196130 [file] [log] [blame]
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))