All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dfhdl.core.DFBits.scala Maven / Gradle / Ivy

package dfhdl.core
import dfhdl.compiler.ir
import ir.DFVal.Func.Op as FuncOp
import dfhdl.internals.*

import scala.annotation.{implicitNotFound, targetName}
import scala.quoted.*
import scala.util.boundary, boundary.break
import DFDecimal.Constraints.`LW == RW`

type DFBits[W <: IntP] = DFType[ir.DFBits, Args1[W]]
object DFBits:
  def apply[W <: IntP](width: IntParam[W])(using
      dfc: DFC,
      check: Arg.Width.CheckNUB[W]
  ): DFBits[W] = trydf:
    check(width)
    ir.DFBits(width.ref).asFE[DFBits[W]]
  def forced[W <: IntP](width: Int): DFBits[W] =
    val check = summon[Arg.Width.Check[Int]]
    check(width)
    ir.DFBits(width).asFE[DFBits[W]]
  def apply[W <: IntP](using dfc: DFC, dfType: => DFBits[W]): DFBits[W] = trydf { dfType }
  def until[V <: IntP](sup: IntParam[V])(using
      dfc: DFC,
      check: Arg.LargerThan1.CheckNUB[V]
  ): DFBits[IntP.CLog2[V]] = trydf:
    check(sup)
    ir.DFBits(sup.clog2.ref).asFE[DFBits[IntP.CLog2[V]]]
  def to[V <: IntP](max: IntParam[V])(using
      dfc: DFC,
      check: Arg.Positive.CheckNUB[V]
  ): DFBits[IntP.CLog2[IntP.+[V, 1]]] = trydf:
    check(max)
    ir.DFBits((max + 1).clog2.ref).asFE[DFBits[IntP.CLog2[IntP.+[V, 1]]]]

  given [W <: IntP & Singleton](using
      dfc: DFC,
      v: ValueOf[W],
      check: Arg.Width.CheckNUB[W]
  ): DFBits[W] =
    val width = IntParam.forced(v)
    check(width.toScalaInt)
    ir.DFBits(width.ref).asFE[DFBits[W]]

  protected object `AW == TW`
      extends Check2[
        Int,
        Int,
        [AW <: Int, TW <: Int] =>> AW == TW,
        [AW <: Int, TW <: Int] =>> "The alias width (" + AW +
          ") is different than the DFHDL value width (" + TW + ")."
      ]
  protected object `LW >= RW`
      extends Check2[
        Int,
        Int,
        [LW <: Int, RW <: Int] =>> LW >= RW,
        [LW <: Int, RW <: Int] =>> "The new width (" + RW +
          ") is larger than the original width (" + LW + ")."
      ]
  protected[core] object BitIndex
      extends Check2[
        Int,
        Int,
        [I <: Int, W <: Int] =>> (I < W) && (I >= 0),
        [I <: Int, W <: Int] =>> "Index " + I + " is out of range of width/length " + W
      ]
  protected object BitsHiLo
      extends Check2[
        Int,
        Int,
        [H <: Int, L <: Int] =>> H >= L,
        [H <: Int, L <: Int] =>> "Low index " + L + " is bigger than High bit index " + H
      ]
  trait CompareCheck[
      ValW <: IntP,
      ArgW <: IntP,
      Castle <: Boolean // castling of dfVal and arg
  ]:
    def apply(dfValWidth: Int, argWidth: Int): Unit
  object CompareCheck:
    given [
        ValW <: IntP,
        ValWI <: Int,
        ArgW <: IntP,
        ArgWI <: Int,
        Castle <: Boolean
    ](using
        ubv: UBound.Aux[Int, ValW, ValWI],
        uba: UBound.Aux[Int, ArgW, ArgWI],
        lw: Id[ITE[Castle, ArgWI, ValWI]],
        rw: Id[ITE[Castle, ValWI, ArgWI]]
    )(using
        checkW: `LW == RW`.Check[lw.Out, rw.Out],
        castle: ValueOf[Castle]
    ): CompareCheck[ValW, ArgW, Castle] with
      def apply(dfValWidth: Int, argWidth: Int): Unit =
        val lw = if (castle) argWidth else dfValWidth
        val rw = if (castle) dfValWidth else argWidth
        checkW(lw, rw)
    end given
  end CompareCheck

  object StrInterp:
    private[DFBits] val widthExp = "([0-9]+)'(.*)".r
    def fromBinString(
        bin: String
    ): Either[String, (BitVector, BitVector)] = boundary {
      val (valueBits, bubbleBits) =
        bin.foldLeft((BitVector.empty, BitVector.empty)) {
          case (t, '_' | ' ') => t // ignoring underscore or space
          case ((v, b), c) =>
            c match // bin mode
              case '?' => (v :+ false, b :+ true)
              case '0' => (v :+ false, b :+ false)
              case '1' => (v :+ true, b :+ false)
              case x =>
                break(Left(s"Found invalid binary character: $x"))
        }
      Right((valueBits, bubbleBits))
    }
    private[DFBits] val isHex = "[0-9a-fA-F]".r
    def fromHexString(
        hex: String
    ): Either[String, (BitVector, BitVector)] = boundary {
      val (valueBits, bubbleBits, binMode) =
        hex.foldLeft((BitVector.empty, BitVector.empty, false)) {
          case (t, '_' | ' ') => t // ignoring underscore or space
          case ((v, b, false), c) =>
            c match // hex mode
              case '{' => (v, b, true)
              case '?' => (v ++ BitVector.low(4), b ++ BitVector.high(4), false)
              case isHex() =>
                (
                  v ++ BitVector.fromHex(c.toString).get,
                  b ++ BitVector.low(4),
                  false
                )
              case x =>
                break(Left(s"Found invalid hex character: $x"))
          case ((v, b, true), c) =>
            c match // bin mode
              case '}' => (v, b, false)
              case '?' => (v :+ false, b :+ true, true)
              case '0' => (v :+ false, b :+ false, true)
              case '1' => (v :+ true, b :+ false, true)
              case x =>
                break(Left(s"Found invalid binary character in binary mode: $x"))
        }
      if (binMode) Left(s"Missing closing braces of binary mode")
      else Right((valueBits, bubbleBits))
    }

    extension (fullTerm: String)
      private[DFBits] def interpolate[W <: IntP](
          op: String,
          explicitWidthOption: Option[IntP]
      )(using DFC): DFConstOf[DFBits[W]] =
        val fromString = op match
          case "b" => fromBinString(fullTerm)
          case "h" => fromHexString(fullTerm)
        var (valueBits, bubbleBits) = fromString.toOption.get
        explicitWidthOption.foreach(ew =>
          val updatedWidth = IntParam.forced(ew).toScalaInt
          valueBits = valueBits.resize(updatedWidth)
          bubbleBits = bubbleBits.resize(updatedWidth)
        )
        val width = IntParam.forced[W](explicitWidthOption.getOrElse(valueBits.length.toInt))
        DFVal.Const(DFBits(width), (valueBits, bubbleBits), named = true)

    extension (using Quotes)(fullTerm: quotes.reflect.Term)
      private[DFBits] def interpolate(
          opExpr: Expr[String],
          explicitWidthOptionExpr: Expr[Option[IntP]]
      ): Expr[DFConstAny] =
        import quotes.reflect.*
        val explicitWidthTpeOption: Option[TypeRepr] = explicitWidthOptionExpr match
          case '{ Some($expr) } => Some(expr.asTerm.tpe)
          case _                => None
        val interpWidthTpe: TypeRepr = fullTerm match
          case Literal(StringConstant(t)) =>
            val opStr = opExpr.value.get
            val res = opStr match
              case "b" => fromBinString(t)
              case "h" => fromHexString(t)
            res match
              case Right((valueBits, bubbleBits)) =>
                explicitWidthTpeOption match
                  case Some(ConstantType(IntConstant(explicitWidth))) =>
                    val actualWidth = valueBits.lengthOfValue.toInt
                    if (explicitWidth < actualWidth)
                      report.errorAndAbort(
                        s"Explicit given width ($explicitWidth) is smaller than the actual width ($actualWidth)."
                      )
                  case _ =>
                ConstantType(IntConstant(valueBits.length.toInt))
              case Left(msg) =>
                report.errorAndAbort(msg)
          case _ => TypeRepr.of[Int]
        val widthTpe: TypeRepr = explicitWidthTpeOption.getOrElse(interpWidthTpe)
        val widthType = widthTpe.asTypeOf[IntP]
        val fullExpr = fullTerm.asExprOf[String]
        '{
          val dfc = compiletime.summonInline[DFC]
          $fullExpr.interpolate[widthType.Underlying](
            $opExpr,
            $explicitWidthOptionExpr
          )(using dfc)
        }

  end StrInterp

  // Unclear why, but the compiler crashes if we do not separate these definitions from StrInterp
  object StrInterpOps:
    import StrInterp.{fromBinString, fromHexString, interpolate, isHex, widthExp}
    opaque type BinStrCtx <: StringContext = StringContext
    object BinStrCtx:
      extension (inline sc: BinStrCtx)
        transparent inline def apply(inline args: Any*): Any =
          ${ applyMacro('sc, 'args) }
        transparent inline def unapplySeq[T <: DFTypeAny](
            inline arg: DFValOf[T]
        )(using DFC): Option[Seq[Any]] =
          ${ unapplySeqMacro('sc, 'arg) }

    extension (sc: StringContext)
      /** Binary Bits Vector String Interpolator
        *
        * Syntax: {{{b"width'bin"}}}
        *   - `bin` is a sequence of '0', '1', and '?' characters, indicating a bit bubble.
        *   - Separators ' ' (space) or '_' (underscore) within `bin` are ignored.
        *   - `width`, followed by a `'`, is optional and specifies the bit vector's width. If
        *     omitted, the width is inferred from the sequence length. If specified, leading zeros
        *     are added or the sequence is truncated based on the `width`. Truncation only occurs if
        *     the most significant bits being removed are zeros; otherwise, it triggers a
        *     compilation error.
        *
        * @example
        *   {{{
        *   b"1"        // Value = 1
        *   b"1000"     // Value = 1000
        *   b"8'1000"   // Value = 00001000
        *   b"3'0100"   // Value = 100
        *   b"3'1100"   // Compilation error
        *   b"1?11"     // Value = 1?11 (? indicates a bit bubble)
        *   b"11_00"    // Value = 1100
        *   }}}
        *
        * @note
        *   This interpolator does not accept external arguments through `${arg}`.
        * @return
        *   A DFHDL Bits vector.
        */
      def b: BinStrCtx = sc

      /** Hexadecimal Bits Vector String Interpolator
        *
        * Syntax: {{{h"width'hex"}}}
        *   - `hex` is a sequence of hexadecimal characters ('0'-'9', 'A'-'F', 'a'-'f', and '?')
        *     where '?' indicates a 4-bit bubble. Each character represents a 4-bit nibble.
        *   - Separators ' ' (space) or '_' (underscore) within `hex` are ignored.
        *   - Binary sequences can be embedded within `{bin}` tags, allowing integration of binary
        *     bit sequences of any length, not necessarily divisible by 4, between hex nibbles.
        *   - `width`, followed by a `'`, is optional and specifies the bit vector's width. If
        *     omitted, the width is inferred from the sequence length. If specified, leading zeros
        *     are added or the sequence is truncated based on the `width`. Truncation only occurs if
        *     the most significant bits being removed are zeros; otherwise, it triggers a
        *     compilation error.
        *
        * @example
        *   {{{
        *   h"1"        // Value = 0001
        *   h"27"       // Value = 00100111
        *   h"6'27"     // Value = 100111
        *   h"5'27"     // Compilation error
        *   h"2?"       // Value = 0010????
        *   h"F{00}F"   // Value = 1111001111
        *   h"3_3"      // Value = 00110011
        *   }}}
        *
        * @note
        *   This interpolator does not accept external arguments through `${arg}`.
        * @return
        *   A DFHDL Bits vector.
        */
      def h: BinStrCtx = sc
    end extension

    private def applyMacro(
        sc: Expr[BinStrCtx],
        args: Expr[Seq[Any]]
    )(using Quotes): Expr[DFConstAny] =
      import quotes.reflect.*
      var Varargs(argsExprs) = args: @unchecked

      var parts = sc.parts.map(_.value.get).toList
      var explicitWidthOption: Expr[Option[IntP]] = '{ None }
      parts match
        case "" :: p :: _ if p.startsWith("'") =>
          argsExprs.headOption.map(_.asTerm) match
            case Some(t) =>
              t.tpe.asType match
                case '[IntP] =>
                  argsExprs = argsExprs.drop(1)
                  parts = p.drop(1) :: parts.drop(2)
                  explicitWidthOption = '{ Some(${ t.asExprOf[IntP] }) }
                case '[DFValAny] =>
                  report.errorAndAbort(
                    s"Expecting a constant DFHDL Int value but found: `${t.tpe.showType}`",
                    t.pos
                  )
                case _ =>
                  report.errorAndAbort(
                    s"Unsupported type as the width interpolation argument. Found: `${t.tpe.showType}`",
                    t.pos
                  )

            case _ =>
        case widthExp(widthStr, wordStr) :: rest =>
          parts = wordStr :: rest
          explicitWidthOption = '{ Some(${ Expr(widthStr.toInt) }) }
        case _ =>
      end match
      // println(widthParamOption.map(_.show))
      parts.map(Expr(_)).scPartsWithArgs(argsExprs).interpolate(
        Expr(sc.funcName),
        explicitWidthOption
      )
    end applyMacro

    private def unapplySeqMacro[T <: DFTypeAny](
        sc: Expr[BinStrCtx],
        arg: Expr[DFValOf[T]]
    )(using Quotes, Type[T]): Expr[Option[Seq[Any]]] =
      import quotes.reflect.*
      val parts = sc.parts
      val partsStr = parts.map(_.value.get).toList
      val op = sc.funcName
      val opExpr = Expr(op)
      if (partsStr.length > 1)
        val vArgs = Varargs(opExpr :: partsStr.map { part =>
          val partFiltered = part.filter {
            case '_' | ' ' | '?'        => false
            case isHex() if op == "h"   => true
            case '0' | '1' if op == "b" => true
            case x =>
              report.errorAndAbort(
                s"""|Found invalid character: ${x}.
                    |Note: string interpolation with value extraction does not support the `[w']` width extension syntax.""".stripMargin
              )
          }
          Expr(partFiltered)
        })
        '{
          Some(Seq(${ vArgs }*))
        }
      else
        val dfVal = partsStr.head match
          case widthExp(widthStr, wordStr) =>
            Literal(StringConstant(wordStr)).interpolate(
              opExpr,
              '{ Some(${ Expr(widthStr.toInt) }) }
            )
          case _ => parts.head.asTerm.interpolate(opExpr, '{ None })

        val dfValType = dfVal.asTerm.tpe.asTypeOf[DFConstAny]
        '{
          val tc = compiletime.summonInline[
            DFVal.Compare[T, dfValType.Underlying, FuncOp.===.type, false]
          ]
          Some(
            Seq(
              tc.conv(${ arg }.dfType, $dfVal)(using compiletime.summonInline[DFC])
            )
          )
        }
      end if
    end unapplySeqMacro
  end StrInterpOps

  object Val:
    trait Candidate[R]:
      type OutW <: IntP
      type OutP
      type Out = DFValTP[DFBits[OutW], OutP]
      def apply(value: R)(using DFC): Out
    object Candidate:
      type Aux[R, W <: IntP, P] = Candidate[R] { type OutW = W; type OutP = P }
      inline given errorOnInt[V <: Int]: Candidate[V] =
        compiletime.error(
          "An integer value cannot be a candidate for a Bits type.\nTry explicitly using a decimal constant via the `d\"'\"` string interpolation."
        )
      given fromDFBits[W <: IntP, P, R <: DFValTP[DFBits[W], P]]: Candidate[R] with
        type OutW = W
        type OutP = P
        def apply(value: R)(using DFC): Out = value
      given fromDFBoolOrBit[P, R <: DFValTP[DFBoolOrBit, P]]: Candidate[R] with
        type OutW = 1
        type OutP = P
        def apply(value: R)(using DFC): Out =
          import DFVal.Ops.bits
          value.bits
      given fromDFUInt[W <: IntP, P, R <: DFValTP[DFUInt[W], P]]: Candidate[R] with
        type OutW = W
        type OutP = P
        def apply(value: R)(using DFC): Out =
          import DFVal.Ops.bits
          if (value.hasTag[DFVal.TruncateTag]) value.bits.tag(DFVal.TruncateTag)
          else if (value.hasTag[DFVal.ExtendTag]) value.bits.tag(DFVal.ExtendTag)
          else value.bits
      inline given errDFEncoding[E <: DFEncoding]: Candidate[E] =
        compiletime.error(
          "Cannot apply an enum entry value to a bits variable."
        )
      // TODO: IntP
      inline given errDFSInt[W <: IntP, R <: DFValOf[DFSInt[W]]]: Candidate[R] =
        compiletime.error(
          "Cannot apply a signed value to a bits variable.\nConsider applying `.bits` conversion to resolve this issue."
        )

      private[Val] def valueToBits(value: Any)(using dfc: DFC): DFValOf[DFBits[Int]] =
        import DFBits.Val.Ops.concatBits
        val dfcAnon = dfc.anonymize
        value match
          case x: NonEmptyTuple =>
            x.toList.map(x => valueToBits(x)(using dfcAnon)).concatBits
          case i: Int =>
            DFVal.Const(DFBits(1), (BitVector.bit(i > 0), BitVector.zero), named = true)
          case dfVal: DFVal[?, ?] =>
            import DFVal.Ops.bits
            val dfValIR = dfVal.asIR
            dfValIR.dfType match
              case _: ir.DFBits => dfValIR.asValOf[DFBits[Int]]
              case _ =>
                dfValIR.asValAny.bits(using Width.wide).asValOf[DFBits[Int]]
        end match
      end valueToBits
      transparent inline given fromTuple[R <: NonEmptyTuple]: Candidate[R] = ${ DFBitsMacro[R] }
      def DFBitsMacro[R](using
          Quotes,
          Type[R]
      ): Expr[Candidate[R]] =
        import quotes.reflect.*
        import Width.*
        val rTpe = TypeRepr.of[R]
        val wType = rTpe.calcValWidth.asTypeOf[Int]
        val pType = rTpe.isConstTpe.asTypeOf[Any]
        '{
          new Candidate[R]:
            type OutW = wType.Underlying
            type OutP = pType.Underlying
            def apply(value: R)(using DFC): Out =
              valueToBits(value).asValTP[DFBits[OutW], pType.Underlying]
        }
      end DFBitsMacro
    end Candidate

    object TC:
      import DFVal.TC
      def apply(
          dfType: DFBits[Int],
          dfVal: DFValOf[DFBits[Int]]
      )(using DFC): DFValOf[DFBits[Int]] =
        `LW == RW`(dfType.widthInt, dfVal.widthInt)
        dfVal
      protected object `LW == RW`
          extends Check2[
            Int,
            Int,
            [LW <: Int, RW <: Int] =>> LW == RW,
            [LW <: Int, RW <: Int] =>> "The argument width (" + ToString[RW] +
              ") is different than the receiver width (" + ToString[LW] +
              ").\nConsider applying `.resize` to resolve this issue."
          ]
      given DFBitsFromCandidate[LW <: IntP, V, IC <: Candidate[V]](using ic: IC)(using
          check: `LW == RW`.CheckNUB[LW, ic.OutW]
      ): TC[DFBits[LW], V] with
        type OutP = ic.OutP
        def conv(dfType: DFBits[LW], value: V)(using dfc: DFC): Out =
          import Ops.resizeBits
          val dfVal = ic(value)
          (dfType.asIR: ir.DFType) match
            case ir.DFNothing =>
              dfVal.nameInDFCPosition.asValTP[DFBits[LW], ic.OutP]
            case _ =>
              if (dfVal.hasTag[DFVal.TruncateTag] && dfType.widthInt < dfVal.widthInt)
                dfVal.resizeBits(dfType.widthIntParam).asValTP[DFBits[LW], ic.OutP]
              else if (dfVal.hasTag[DFVal.ExtendTag] && dfType.widthInt > dfVal.widthInt)
                dfVal.resizeBits(dfType.widthIntParam).asValTP[DFBits[LW], ic.OutP]
              else
                check(dfType.widthInt, dfVal.widthInt)
                dfVal.nameInDFCPosition.asValTP[DFBits[LW], ic.OutP]
        end conv
      end DFBitsFromCandidate
      given DFBitsFromSEV[LW <: IntP, T <: BitOrBool, V <: SameElementsVector[T]]: TC[DFBits[LW], V]
      with
        type OutP = CONST
        def conv(dfType: DFBits[LW], value: V)(using DFC): Out =
          SameElementsVector.bitsValOf(dfType.widthIntParam, value, named = true)
            .asConstOf[DFBits[LW]]
    end TC

    object Compare:
      import DFVal.Compare
      given DFBitsCompareCandidate[LW <: IntP, R, IC <: Candidate[R], Op <: FuncOp, C <: Boolean](
          using ic: IC
      )(using
          check: CompareCheck[LW, ic.OutW, C],
          op: ValueOf[Op],
          castling: ValueOf[C]
      ): Compare[DFBits[LW], R, Op, C] with
        type OutP = ic.OutP
        def conv(dfType: DFBits[LW], arg: R)(using DFC): Out =
          val dfValArg = ic(arg)
          check(dfType.widthInt, dfValArg.dfType.widthInt)
          dfValArg.asValTP[DFBits[LW], ic.OutP]
      given DFBitsCompareSEV[
          LW <: IntP,
          Op <: FuncOp,
          C <: Boolean,
          T <: BitOrBool,
          V <: SameElementsVector[T]
      ](using
          ValueOf[Op],
          ValueOf[C]
      ): Compare[DFBits[LW], V, Op, C] with
        type OutP = CONST
        def conv(dfType: DFBits[LW], arg: V)(using DFC): Out =
          SameElementsVector.bitsValOf(dfType.widthIntParam, arg, named = true)
            .asConstOf[DFBits[LW]]
      end DFBitsCompareSEV
    end Compare

    // this was defined separately from `Ops` to avoid collision with `.bits` used in `Ops`
    object TupleOps:
      // explicit conversion of a tuple to bits (concatenation)
      extension (inline tpl: NonEmptyTuple)
        transparent inline def toBits: Any = ${ bitsMacro('tpl) }
      private def bitsMacro(tpl: Expr[NonEmptyTuple])(using Quotes): Expr[Any] =
        import quotes.reflect.*
        val tplTerm = tpl.asTerm.exactTerm
        import Width.*
        val rTpe = tplTerm.tpe
        val pType = rTpe.isConstTpe.asTypeOf[Any]
        val wType = rTpe.calcValWidth.asTypeOf[Int]
        '{
          Val.Candidate
            .valueToBits($tpl)(using compiletime.summonInline[DFC])
            .asValTP[DFBits[wType.Underlying], pType.Underlying]
        }
    end TupleOps

    object Ops:
      extension [W <: IntP, P](lhs: DFValTP[DFBits[W], P])
        def truncate(using DFC): DFValTP[DFBits[Int], P] =
          lhs.tag(DFVal.TruncateTag).asValTP[DFBits[Int], P]
        // TODO: IntP
        private[DFBits] def resizeBits[RW <: IntP](updatedWidth: IntParam[RW])(using
            DFC
        ): DFValTP[DFBits[RW], P] =
          // TODO: why this causes anonymous references?
//          if (lhs.width == updatedWidth) lhs.asValOf[DFBits[RW]]
//          else
          DFVal.Alias.AsIs(DFBits(updatedWidth), lhs)
      end extension
      extension [T <: Int, P](iter: Iterable[DFValTP[DFBits[T], P]])
        protected[core] def concatBits(using DFC): DFValTP[DFBits[Int], P] =
          val width =
            iter.map(_.widthIntParam.asInstanceOf[IntParam[Int]]).reduce(_ + _)
          DFVal.Func(DFBits(width), FuncOp.++, iter.toList)
      // only Bits and UInt as expected candidates
      extension [W0 <: IntP, L <: DFValOf[DFBits[W0]] | DFValOf[DFUInt[W0]], LW <: IntP, LP](
          lhs: L
      )(using
          icL: Candidate[L]
      )
        def &[R](rhs: Exact[R])(using icR: Candidate[R])(using
            dfc: DFC,
            check: `LW == RW`.CheckNUB[icL.OutW, icR.OutW]
        ): DFValTP[DFBits[icL.OutW], icL.OutP | icR.OutP] = trydf {
          val lhsVal = icL(lhs)
          val rhsVal = icR(rhs)
          check(lhsVal.widthInt, rhsVal.widthInt)
          DFVal.Func(lhsVal.dfType, FuncOp.&, List(lhsVal, rhsVal))
        }
        def |[R](rhs: Exact[R])(using icR: Candidate[R])(using
            dfc: DFC,
            check: `LW == RW`.CheckNUB[icL.OutW, icR.OutW]
        ): DFValTP[DFBits[icL.OutW], icL.OutP | icR.OutP] = trydf {
          val lhsVal = icL(lhs)
          val rhsVal = icR(rhs)
          check(lhsVal.widthInt, rhsVal.widthInt)
          DFVal.Func(lhsVal.dfType, FuncOp.|, List(lhsVal, rhsVal))
        }
        def ^[R](rhs: Exact[R])(using icR: Candidate[R])(using
            dfc: DFC,
            check: `LW == RW`.CheckNUB[icL.OutW, icR.OutW]
        ): DFValTP[DFBits[icL.OutW], icL.OutP | icR.OutP] = trydf {
          val lhsVal = icL(lhs)
          val rhsVal = icR(rhs)
          check(lhsVal.widthInt, rhsVal.widthInt)
          DFVal.Func(lhsVal.dfType, FuncOp.^, List(lhsVal, rhsVal))
        }
        // reduction AND of all bits
        def &(using dfc: DFC): DFValTP[DFBit, icL.OutP] = trydf {
          DFVal.Func(DFBit, FuncOp.&, List(icL(lhs)))
        }
        // reduction OR of all bits
        def |(using dfc: DFC): DFValTP[DFBit, icL.OutP] = trydf {
          DFVal.Func(DFBit, FuncOp.|, List(icL(lhs)))
        }
        // reduction XOR of all bits
        def ^(using dfc: DFC): DFValTP[DFBit, icL.OutP] = trydf {
          DFVal.Func(DFBit, FuncOp.^, List(icL(lhs)))
        }
      end extension
      extension [L <: DFValAny, LW <: IntP, LP](lhs: L)(using icL: Candidate.Aux[L, LW, LP])
        def extend(using DFC): DFValTP[DFBits[Int], icL.OutP] =
          icL(lhs).tag(DFVal.ExtendTag).asValTP[DFBits[Int], icL.OutP]
        def resize[RW <: IntP](updatedWidth: IntParam[RW])(using
            check: Arg.Width.CheckNUB[RW],
            dfc: DFC
        ): DFValTP[DFBits[RW], icL.OutP] = trydf {
          check(updatedWidth)
          icL(lhs).resizeBits(updatedWidth)
        }
        def repeat[N <: Int](num: IntParam[N])(using
            dfc: DFC,
            check: Arg.Positive.Check[N]
        ): DFValTP[DFBits[IntP.*[icL.OutW, N]], icL.OutP | CONST] = trydf {
          val lhsVal = icL(lhs)
          check(num)
          val lhsWidth = lhsVal.widthIntParam
          val width =
            // simplifying the representation if the argument is a single bit
            if (lhsWidth.toScalaInt == 1) num.asInstanceOf[IntParam[IntP.*[icL.OutW, N]]]
            else lhsWidth * num
          DFVal.Func(DFBits(width), FuncOp.repeat, List(lhsVal, num.toDFConst))
        }
        def ++[R](rhs: Exact[R])(using icR: Candidate[R])(using
            dfc: DFC
        ): DFValTP[DFBits[IntP.+[icL.OutW, icR.OutW]], icL.OutP | icR.OutP] = trydf {
          val lhsVal = icL(lhs)
          val rhsVal = icR(rhs)
          val width = lhsVal.widthIntParam + rhsVal.widthIntParam
          DFVal.Func(DFBits(width), FuncOp.++, List(lhsVal, rhsVal))
        }
      end extension

      extension [W <: IntP, A, C, I, P](
          lhs: DFVal[DFBits[W], Modifier[A, C, I, P]]
      )
        def as[AT <: DFType.Supported](
            aliasType: AT
        )(using tc: DFType.TC[AT])(using
            aW: Width[tc.Type],
            dfc: DFC
        )(using check: `AW == TW`.CheckNUB[aW.Out, W]): DFValTP[tc.Type, P] = trydf {
          import dfc.getSet
          val aliasDFType = tc(aliasType)
          check(aliasDFType.asIR.width, lhs.widthInt)
          DFVal.Alias.AsIs(aliasDFType, lhs)
        }
        def uint(using DFC): DFValTP[DFUInt[W], P] = trydf { as(DFUInt(lhs.widthIntParam)) }
        def sint(using DFC): DFValTP[DFSInt[W], P] = trydf { as(DFSInt(lhs.widthIntParam)) }
        def apply[Idx](
            relIdx: Exact[Idx]
        )(using
            c: DFUInt.Val.UBArg[W, Idx],
            dfc: DFC
        ): DFVal[DFBit, Modifier[A, Any, Any, P]] = trydf {
          DFVal.Alias.ApplyIdx(DFBit, lhs, c(lhs.widthIntParam, relIdx)(using dfc.anonymize))
        }
        def apply[H <: Int, L <: Int](
            relBitHigh: Inlined[H],
            relBitLow: Inlined[L]
        )(using
            checkHigh: BitIndex.CheckNUB[H, W],
            checkLow: BitIndex.CheckNUB[L, W],
            checkHiLo: BitsHiLo.Check[H, L],
            dfc: DFC
        ): DFVal[DFBits[H - L + 1], Modifier[A, Any, Any, P]] = trydf {
          checkHigh(relBitHigh, lhs.widthInt)
          checkLow(relBitLow, lhs.widthInt)
          checkHiLo(relBitHigh, relBitLow)
          DFVal.Alias.ApplyRange(lhs, relBitHigh, relBitLow)
        }
        def unary_~(using DFC): DFValTP[DFBits[W], P] = trydf {
          DFVal.Func(lhs.dfType, FuncOp.unary_~, List(lhs))
        }
        def msbit(using DFC): DFVal[DFBit, Modifier[A, Any, Any, P]] =
          lhs.apply(lhs.widthInt.value - 1)
        def lsbit(using DFC): DFVal[DFBit, Modifier[A, Any, Any, P]] =
          lhs.apply(0)
        // TODO: IntP
        def msbits[RW <: Int](updatedWidth: Inlined[RW])(using
            check: `LW >= RW`.CheckNUB[W, RW],
            dfc: DFC
        ): DFValTP[DFBits[RW], P] = trydf {
          check(lhs.widthInt, updatedWidth)
          DFVal.Alias.ApplyRange(lhs, lhs.widthInt - 1, lhs.widthInt - updatedWidth)
            .asValTP[DFBits[RW], P]
        }
        // TODO: IntP
        def lsbits[RW <: Int](updatedWidth: Inlined[RW])(using
            check: `LW >= RW`.CheckNUB[W, RW],
            dfc: DFC
        ): DFValTP[DFBits[RW], P] = trydf {
          check(lhs.widthInt, updatedWidth)
          DFVal.Alias.ApplyRange(lhs, updatedWidth - 1, 0)
            .asValTP[DFBits[RW], P]
        }
        @targetName("shiftRightDFBits")
        def >>[R](shift: Exact[R])(using
            c: DFUInt.Val.UBArg[W, R],
            dfc: DFC
        ): DFValOf[DFBits[W]] = trydf {
          val shiftVal = c(lhs.widthIntParam, shift)(using dfc.anonymize)
          DFVal.Func(lhs.dfType, FuncOp.>>, List(lhs, shiftVal))
        }
        @targetName("shiftLeftDFBits")
        def <<[R](shift: Exact[R])(using
            c: DFUInt.Val.UBArg[W, R],
            dfc: DFC
        ): DFValOf[DFBits[W]] = trydf {
          val shiftVal = c(lhs.widthIntParam, shift)(using dfc.anonymize)
          DFVal.Func(lhs.dfType, FuncOp.<<, List(lhs, shiftVal))
        }
      end extension
      extension [L](lhs: L)
        def ++[RW <: IntP, RP](
            rhs: DFValTP[DFBits[RW], RP]
        )(using es: Exact.Summon[L, lhs.type])(using
            dfc: DFC,
            c: Candidate[es.Out]
        ): DFValTP[DFBits[IntP.+[c.OutW, RW]], c.OutP | RP] = trydf {
          val lhsVal = c(es(lhs))
          val width = lhsVal.widthIntParam + rhs.widthIntParam
          DFVal.Func(DFBits(width), FuncOp.++, List(lhsVal, rhs))
        }
        def &[RW <: IntP, RP](
            rhs: DFValTP[DFBits[RW], RP]
        )(using es: Exact.Summon[L, lhs.type])(using
            dfc: DFC,
            c: Candidate[es.Out]
        )(using check: `LW == RW`.CheckNUB[c.OutW, RW]): DFValTP[DFBits[RW], c.OutP | RP] = trydf {
          val lhsVal = c(es(lhs))
          check(lhsVal.widthInt, rhs.widthInt)
          DFVal.Func(rhs.dfType, FuncOp.&, List(lhsVal, rhs))
        }
        def |[RW <: IntP, RP](
            rhs: DFValTP[DFBits[RW], RP]
        )(using es: Exact.Summon[L, lhs.type])(using
            dfc: DFC,
            c: Candidate[es.Out]
        )(using check: `LW == RW`.CheckNUB[c.OutW, RW]): DFValTP[DFBits[RW], c.OutP | RP] = trydf {
          val lhsVal = c(es(lhs))
          check(lhsVal.widthInt, rhs.widthInt)
          DFVal.Func(rhs.dfType, FuncOp.|, List(lhsVal, rhs))
        }
        def ^[RW <: IntP, RP](
            rhs: DFValTP[DFBits[RW], RP]
        )(using es: Exact.Summon[L, lhs.type])(using
            dfc: DFC,
            c: Candidate[es.Out]
        )(using check: `LW == RW`.CheckNUB[c.OutW, RW]): DFValTP[DFBits[RW], c.OutP | RP] = trydf {
          val lhsVal = c(es(lhs))
          check(lhsVal.widthInt, rhs.widthInt)
          DFVal.Func(rhs.dfType, FuncOp.^, List(lhsVal, rhs))
        }
      end extension

    end Ops
  end Val
end DFBits




© 2015 - 2024 Weber Informatics LLC | Privacy Policy