+
+class ArrayObject(DataObject):
+ def __init__(self, record,
+ nested_array = False,
+ el_factory = lambda x: DataObject.create("", x)):
+ self._el_factory = el_factory
+ self._nested_array = nested_array
+
+ super().__init__("", record)
+
+ def _parse(self, record):
+ if not isinstance(record, list):
+ raise Exception(f"{type(self).__name__} require array input")
+
+ self.content = []
+ for x in record:
+ self.content.append(self._el_factory(x))
+
+ def expand(self, param={}):
+ result = []
+ for x in self.content:
+ obj = x.expand(param)
+ if isinstance(obj, list) and not self._nested_array:
+ result += [*obj]
+ else:
+ result.append(obj)
+
+ return result
+
+class MemoryMapObject(DataObject):
+ class GranuleObject(DataObject):
+ def __init__(self, record):
+ super().__init__("", record)
+
+ def _parse(self, record):
+ self.__granules = {}
+ for k, v in record.items():
+ self.__granules[k] = DataObject.create(k, v)
+
+ def expand(self, param={}):
+ granules = {}
+ for k, v in self.__granules.items():
+ val = v.expand(param)
+
+ if not isinstance(val, int):
+ raise Exception("The granule definition must be either integer or int-castable string")
+
+ granules[k] = val
+
+ return {**granules}
+
+ def __init__(self, record):
+ super().__init__("", record)
+
+ def _parse(self, record):
+ for k, v in record.items():
+ if k.startswith("$"):
+ self.ctrl_field[k.strip("$")] = FieldType.create(k, v)
+ elif k.startswith("@"):
+ self.ctrl_field[k.strip("@")] = DataObject.create(k, v)
+
+ if "granule" in record:
+ self.__g = MemoryMapObject.GranuleObject(record["granule"])
+
+ if "regions" in record:
+ self.__regions = ArrayObject(record["regions"])
+
+ if "width" in record:
+ self.__width = DataObject.create("width", record["width"])
+
+ def __process(self, start_addr, idx, regions, size_lookahead = False):
+ if idx >= len(regions):
+ raise Exception("Unbounded region definition")
+
+ e = regions[idx]
+
+ if "block" in e:
+ b = e["block"] - 1
+ start_addr = (start_addr + b) & ~b
+
+ if "start" not in e:
+ e["start"] = start_addr
+ elif e["start"] < start_addr:
+ raise Exception(f"starting addr {hex(e['start'])} overrlapping with {hex(start_addr)}")
+ else:
+ start_addr = e["start"]
+
+ if "size" not in e:
+ if size_lookahead:
+ raise Exception("could not infer size from unbounded region")
+ tmp_addr = self.__process(start_addr, idx + 1, regions, size_lookahead=True)
+ e["size"] = tmp_addr - start_addr
+
+ if not size_lookahead:
+ start_addr += e["size"]
+
+ return start_addr
+
+ def expand(self, param={}):
+ super().expand(param)
+
+ g = self.__g.expand(param)
+
+ param["granule"] = g
+
+ width = self.__width.expand(param)
+ if not isinstance(width, int):
+ raise Exception("'width' attribute must be integer")
+
+ regions = self.__regions.expand(param)
+
+ start_addr = 0
+ for i in range(len(regions)):
+ start_addr = self.__process(start_addr, i, regions)
+
+ if math.log2(start_addr) > width:
+ raise Exception("memory size larger than speicified address width")
+
+ return {
+ "granule": g,
+ "regions": regions
+ }