Source code for hwt.hdl.types.union

from collections import OrderedDict

from hwt.doc_markers import internal
from hwt.hdl.types.hdlType import HdlType
from hwt.hdl.types.struct import HStructField
from hwt.hdl.value import HValue
from hwt.serializer.generic.indent import getIndent


protectedNames = {"clone", "staticEval",
                  "from_py", "_dtype", "_usedField", "_val"}


[docs]class UnionValBase(HValue): """ Base class for values for union types. Every union type has it's own value class derived from this. :ivar ~._dtype: union type of this value :ivar ~.__usedField: member which is actually used to represent value :ivar ~.__val: value for __usedField """ __slots__ = ["_dtype", "_val", "_usedField"]
[docs] def __init__(self, typeObj, val): """ :param val: None or tuple (member name, member value) :param typeObj: instance of HUnion HdlType for this value """ self._dtype = typeObj if val is not None: memberName, v = val else: memberName = next(iter((typeObj.fields.keys()))) v = None f = self._dtype.fields[memberName] if not isinstance(v, HValue): v = f.dtype.from_py(v) else: v._auto_cast(f.dtype) self._val = v self._usedField = f
[docs] @classmethod def from_py(cls, typeObj, val, vld_mask=None): """ :param val: None or tuple (member name, member value) :param typeObj: instance of HUnion HdlType for this value :param vld_mask: if is None validity is resolved from val if is 0 value is invalidated if is 1 value has to be valid """ if vld_mask == 0: val = None return cls(typeObj, val)
def __repr__(self, indent=0): # [TODO] refactor too similar to StructValBase.__repr__ buff = ["{"] indentOfFields = getIndent(indent + 1) for f in self._dtype.fields.values(): if f.name is not None: val = getattr(self, f.name) try: v = val.__repr__(indent=indent + 1) except TypeError: v = repr(val) buff.append(f"{indentOfFields:s}{f.name:s}: {v:s}") buff.append(getIndent(indent) + "}") return ("\n").join(buff)
[docs]@internal class HUnionMemberHandler(object): """ Object which manages the acces to HUnion field """
[docs] def __init__(self, field): self.field = field
[docs] def set(self, parent, v): f = parent._dtype.fields[self.field.name] if not isinstance(v, HValue): v = f.dtype.from_py(v) else: v._auto_cast(f.dtype) parent._val = v parent._usedField = f
[docs] def get(self, parent): name = self.field.name v = parent._val if parent._usedField.name == name: return v else: f = parent._dtype.fields[name] v = v._reinterpret_cast(f.dtype) parent._val = v parent._usedField = f return v
[docs]class HUnion(HdlType): """ HDL union type (same data multiple representations) :ivar ~.fields: read only OrderedDict {key:StructField} for each member in this union :ivar ~.name: name of this type :ivar ~.__bit_length_val: precalculated bit_length of this type """
[docs] def __init__(self, *template, name=None, const=False): """ :param template: list of tuples (type, name) or HStructField objects name can be None (= padding) :param name: optional name used for debugging purposes """ super(HUnion, self).__init__(const=const) self.fields = OrderedDict() self.field_by_name = self.fields self.name = name bit_length = None class UnionVal(UnionValBase): pass for f in template: try: field = HStructField(*f) except TypeError: field = f if not isinstance(field, HStructField): raise TypeError( "Template for struct field %s is not" " in valid format" % repr(f)) assert field.name is not None self.fields[field.name] = field t = field.dtype if bit_length is None: bit_length = t.bit_length() else: _bit_length = t.bit_length() if _bit_length != bit_length: raise TypeError( field.name, " has different size than others") memberHandler = HUnionMemberHandler(field) p = property(fget=memberHandler.get, fset=memberHandler.set) setattr(UnionVal, field.name, p) self.__bit_length_val = bit_length self.__hash = hash((self.name, tuple(self.fields.items()))) usedNames = set(self.fields.keys()) assert not protectedNames.intersection( usedNames), protectedNames.intersection(usedNames) if name is not None: UnionVal.__name__ = name + "Val" self.valueCls = UnionVal
[docs] def bit_length(self): bl = self.__bit_length_val if bl is None: raise TypeError("Can not request bit_lenght on type" " which has not fixed size") else: return self.__bit_length_val
[docs] @internal def getValueCls(self): return self.valueCls
@internal def __fields__eq__(self, other): if len(self.fields) != len(other.fields): return False for k, sf in self.fields.items(): try: of = other.fields[k] except KeyError: return False if (sf.dtype != of.dtype or sf.meta != of.meta): return False return True def __eq__(self, other): return self is other or ( type(self) is type(other) and self.bit_length() == other.bit_length() and self.__fields__eq__(other)) @internal def __hash__(self): return self.__hash def __repr__(self, indent=0, withAddr=None, expandStructs=False): """ :param indent: number of indentation :param withAddr: if is not None is used as a additional information about on which address this type is stored (used only by HStruct) :param expandStructs: expand HStructTypes (used by HStruct and HArray) """ if self.name: name = self.name + " " else: name = "" myIndent = getIndent(indent) childIndent = getIndent(indent + 1) header = f"{myIndent:s}union {name:s}{{" buff = [header, ] for f in self.fields.values(): if f.name is None: buff.append(f"{childIndent:s}//{f.dtype} empty space") else: buff.append("%s %s" % ( f.dtype.__repr__(indent=indent + 1, withAddr=withAddr, expandStructs=expandStructs), f.name)) buff.append(f"{myIndent:s}}}") return "\n".join(buff)