Source code for hwt.hdl.operatorDefs

from operator import floordiv, add, sub, inv, mul, ne, and_, or_, \
    xor, gt, ge, lt, le, getitem, neg
from typing import Optional

from hdlConvertorAst.hdlAst._expr import HdlOpType
from hwt.doc_markers import internal
from hwt.hdl.const import HConst
from hwt.hdl.types.defs import INT, SLICE


[docs] def _getVal(v): while not isinstance(v, HConst): v = v._val return v
[docs] class HOperatorDef(): """ Operator definition :ivar ~.id: name of operator :ivar ~._evalFn: function which evaluates operands :ivar ~.hdlConvertoAstOp: an operator which is used for export to hdlConvertoAst library """
[docs] def __init__(self, evalFn, allowsAssignTo=False, idStr:Optional[str]=None, hdlConvertoAstOp: Optional[HdlOpType]=None): self.id = idStr # assigned automatically in HwtOps self._evalFn = evalFn self.allowsAssignTo = allowsAssignTo self.hdlConvertoAstOp = hdlConvertoAstOp
def __eq__(self, other): return type(self) == type(other) and self.id == other.id @internal def __hash__(self): return hash(self.id)
[docs] def eval(self, operator, simulator=None): """Load all operands and process them by self._evalFn""" operands = [_getVal(o) for o in operator.operands] return self._evalFn(*operands)
def __repr__(self): return f"<{self.__class__.__name__:s} {self.id:s}>"
[docs] def isEventDependentOp(operator): return operator in (HwtOps.RISING_EDGE, HwtOps.FALLING_EDGE)
[docs] def onRisingEdgeFn(a): return a._onRisingEdge()
[docs] def onFallingEdgeFn(a): return a._onFallingEdge()
[docs] def dotOpFn(a, name): return getattr(a, name)
# [TODO] downto / to are relict of vhdl and should be replaced with slice
[docs] def downtoFn(a: int, b: int): return SLICE.from_py(slice(a, b, -1))
[docs] def toFn(a: int, b: int): return SLICE.from_py(slice(a, b, 1))
[docs] def concatFn(a: "AnyHBitsValue", b: "AnyHBitsValue") -> "AnyHBitsValue": return a._concat(b)
[docs] def power(base, exp): return base ** exp
[docs] def eqFn(a, b): return a._eq(b)
[docs] def ternaryFn(cond: "AnyHBitsValue", vTrue, vFalse): return cond._ternary(vTrue, vFalse)
[docs] def callFn(fn: "HdlFunctionDef", *operands, **kwargs): return fn(*operands, **kwargs)
[docs] def bitsToIntFn(a: "AnyHBitsValue"): return a._auto_cast(INT)
[docs] def intToBitsFn(a: "AnyHBitsValue", t: "HdlType"): return a._auto_cast(t)
[docs] def bitsAsSignedFn(a: "AnyHBitsValue"): return a._signed()
[docs] def bitsAsUnsignedFn(a: "AnyHBitsValue"): return a._unsigned()
[docs] def bitsAsVec(a: "AnyHBitsValue"): return a._vec()
[docs] def zextFn(a: "AnyHBitsValue", newWidth: int): return a._zext(newWidth)
[docs] def sextFn(a: "AnyHBitsValue", newWidth: int): return a._sext(newWidth)
[docs] def truncFn(a: "AnyHBitsValue", newWidth: int): return a._trunc(newWidth)
[docs] class HwtOps(): """ :attention: Remember that and operator "and" is & and "or" is \\|, "and" and "or" can not be used because they can not be overloaded :attention: These are operators of internal AST, they are not equal to verilog or vhdl operators """ RISING_EDGE = HOperatorDef(onRisingEdgeFn) # unnecessary FALLING_EDGE = HOperatorDef(onFallingEdgeFn) # unnecessary MINUS_UNARY = HOperatorDef(neg) DIV = HOperatorDef(floordiv) UDIV = HOperatorDef(lambda a, b: a._unsigned() // b._unsigned()) SDIV = HOperatorDef(lambda a, b: a._signed() // b._signed()) ADD = HOperatorDef(add) SUB = HOperatorDef(sub) POW = HOperatorDef(power) # see https://stackoverflow.com/questions/25848879/difference-between-mod-and-rem-operators-in-vhdl UREM = HOperatorDef(lambda a, b: a._unsigned() % b._unsigned()) # "rem operator" SREM = HOperatorDef(lambda a, b: a._signed() % b._signed()) # "modulo operator" # MUL bit_length and sign of src0, src1 and dst is the same # sign/unsign variant with double result width is recognized from sext/zext of operands in final phases of serialization MUL = HOperatorDef(mul) NOT = HOperatorDef(inv, allowsAssignTo=True) XOR = HOperatorDef(xor) AND = HOperatorDef(and_) OR = HOperatorDef(or_) DOT = HOperatorDef(dotOpFn, allowsAssignTo=True) DOWNTO = HOperatorDef(downtoFn) TO = HOperatorDef(toFn) CONCAT = HOperatorDef(concatFn, allowsAssignTo=True) # :note: SEXT, ZEXT, TRUNC are redundant as it can be implemented using INDEX/CONCAT however they exist # from performance reasons as patern match for them would be very common during optimizations and # specific evaluation functions may be significantly faster # :note: normalization rules: # * SEXT, ZEXT is prefered over concatenation # * sext(a:1b) should be used internally instead of concat(a, a) # * TRUNC is prefered over index with a single exception # * x[0] should be used internally instead of trunc(x, 1) SEXT = HOperatorDef(sextFn) # sign extension of bit vector to larger width ZEXT = HOperatorDef(zextFn) # zero extension of bit vector to larger width TRUNC = HOperatorDef(truncFn, allowsAssignTo=True) # truncate width of bit vector EQ = HOperatorDef(eqFn) NE = HOperatorDef(ne) # :note: for compare operands without U/S the info about sign is stored in type of operands # for U/S variant the signed flag in the type is ignored and signines is forced by operator definition GT = HOperatorDef(gt) GE = HOperatorDef(ge) LT = HOperatorDef(lt) LE = HOperatorDef(le) ULE = HOperatorDef(lambda a, b: a._unsigned() <= b._unsigned()) ULT = HOperatorDef(lambda a, b: a._unsigned() < b._unsigned()) UGT = HOperatorDef(lambda a, b: a._unsigned() > b._unsigned()) UGE = HOperatorDef(lambda a, b: a._unsigned() >= b._unsigned()) SLE = HOperatorDef(lambda a, b: a._signed() <= b._signed()) SLT = HOperatorDef(lambda a, b: a._signed() < b._signed()) SGT = HOperatorDef(lambda a, b: a._signed() > b._signed()) SGE = HOperatorDef(lambda a, b: a._signed() >= b._signed()) # :note: INDEX is used for arrays and also for bit vectors INDEX = HOperatorDef(getitem, allowsAssignTo=True) TERNARY = HOperatorDef(ternaryFn) CALL = HOperatorDef(callFn) BitsAsSigned = HOperatorDef(bitsAsSignedFn, allowsAssignTo=True) BitsAsUnsigned = HOperatorDef(bitsAsUnsignedFn, allowsAssignTo=True) BitsAsVec = HOperatorDef(bitsAsVec, allowsAssignTo=True) BitsFlagCast = HOperatorDef(bitsAsVec, allowsAssignTo=True)
# :note: BitsFlagCast is used to change negated, stric_width, strict_sign flags # internally but it does not have effect on HDL for a_name in dir(HwtOps): o = getattr(HwtOps, a_name) if isinstance(o, HOperatorDef): o.id = a_name CAST_OPS = (HwtOps.BitsAsVec, HwtOps.BitsAsSigned, HwtOps.BitsAsUnsigned, HwtOps.BitsFlagCast) BITWISE_OPS = (HwtOps.NOT, HwtOps.XOR, HwtOps.AND, HwtOps.OR) COMPARE_OPS = ( HwtOps.EQ, HwtOps.NE, HwtOps.GT, HwtOps.GE, HwtOps.LT, HwtOps.LE, HwtOps.ULE, HwtOps.ULT, HwtOps.UGT, HwtOps.UGE, HwtOps.SLE, HwtOps.SLT, HwtOps.SGT, HwtOps.SGE, ) # change of compare operator on operand order swap CMP_OP_SWAP = { HwtOps.EQ: HwtOps.EQ, # (a == b) == (b == a) HwtOps.NE: HwtOps.NE, # (a != b) == (b != a) HwtOps.GT: HwtOps.LT, # (a > b) == (b < a) HwtOps.GE: HwtOps.LE, # (a >= b) == (b <= a) HwtOps.LT: HwtOps.GT, # (a < b) == (b > a) HwtOps.LE: HwtOps.GE, # (a <= b) == (b >= a) HwtOps.UGT: HwtOps.ULT, HwtOps.UGE: HwtOps.ULE, HwtOps.ULT: HwtOps.UGT, HwtOps.ULE: HwtOps.UGE, HwtOps.SGT: HwtOps.SLT, HwtOps.SGE: HwtOps.SLE, HwtOps.SLT: HwtOps.SGT, HwtOps.SLE: HwtOps.SGE, } CMP_OPS_NEG = { HwtOps.EQ: HwtOps.NE, HwtOps.NE: HwtOps.EQ, HwtOps.GT: HwtOps.LE, HwtOps.GE: HwtOps.LT, HwtOps.LT: HwtOps.GE, HwtOps.LE: HwtOps.GT, HwtOps.UGT: HwtOps.ULE, HwtOps.UGE: HwtOps.ULT, HwtOps.ULT: HwtOps.UGE, HwtOps.ULE: HwtOps.UGT, HwtOps.SGT: HwtOps.SLE, HwtOps.SGE: HwtOps.SLT, HwtOps.SLT: HwtOps.SGE, HwtOps.SLE: HwtOps.SGT, } # always commutative operators for which order of operands does not matter ALWAYS_COMMUTATIVE_OPS = (HwtOps.EQ, HwtOps.NE, HwtOps.XOR, HwtOps.AND, HwtOps.OR, HwtOps.ADD, HwtOps.MUL) # always commutative associative operators for which order of operands in expression tree does not matter ALWAYS_ASSOCIATIVE_COMMUTATIVE_OPS = (HwtOps.XOR, HwtOps.AND, HwtOps.OR, HwtOps.ADD, HwtOps.MUL) EVENT_OPS = (HwtOps.RISING_EDGE, HwtOps.FALLING_EDGE)