package loywong

import scala.math._
import chisel3._
import chisel3.util._
import chiseltest._

object util {
    implicit class BitsUtil(private val x: Bits) extends AnyVal {
        /**
         * Get the minimum width divisible by 8 and no less than the data width
         *
         * @return The minimum width divisible by 8
         */
        def getWidthUp8: Int = ((x.getWidth - 1) / 8 + 1) * 8

        /**
         * Get the minimum number of bytes that can hold data
         *
         * @return The minimum number of bytes
         */
        def getBytesCeil: Int = (x.getWidth - 1) / 8 + 1

        /**
         * Extend width by padding 0 to MSB side
         *
         * @param toBits Desired width of return data
         * @return Extended data
         */
        def extend(toBits: Int): Bits = {
            require(toBits >= x.getWidth)
            if (toBits > x.getWidth) {
                0.U((toBits - x.getWidth).W) ## x
            }
            else {
                x
            }
        }

        /**
         * Extend width to integer number of bytes
         *
         * @return Extended data
         */
        def extendToBytes: Bits = {
            x match {
                case u: UInt => u.extendToBytes
                case s: SInt => s.extendToBytes
                case _ => x.extendToBytes
            }
        }

        /**
         * Slice Bits into slices according to width of sinks and connect them
         * to sinks
         *
         * @param sinks Sinks to be connected
         * @example {{{
         *              val x = Wire(UInt(12.W))
         *              val a = Wire(UInt(4.W))
         *              val b = Wire(SInt(8.W))
         *              x.sliceTo(a, b)
         * }}}
         */
        def sliceTo(sinks: Bits*): Unit = {
            var lsb = 0
            sinks.reverse.foreach(y => {
                y match {
                    case s: SInt => s := x(lsb + s.getWidth - 1, lsb).asSInt
                    case u: UInt => u := x(lsb + u.getWidth - 1, lsb)
                    case _ => y := x(lsb + y.getWidth - 1, lsb)
                }
                lsb += y.getWidth
            })
        }

        /**
         * Slice Bits into slices according to widths
         *
         * @param widths Widths of each slices
         * @return Seq of slices
         */
        def slices(widths: Int*): Seq[UInt] = {
            val res = widths.map(w => Wire(UInt(w.W)))
            sliceTo(res: _*)
            res
        }

        /**
         * Concat multiple Bits and connect them to this Bits
         *
         * @param sources The multiple bits to be concatenated
         */
        def catFrom(sources: Bits*): Unit = {
            x := Cat(sources)
        }

        /**
         * Slice Bits into slices according to `widthUp8` of sinks and connect
         * them to sinks
         *
         * @param sinks Sinks to be connected
         * @example {{{
         *              val x = Wire(UInt(24W))
         *              val a = Wire(UInt(6.W))
         *              val b = Wire(UInt(12.W))
         *              // a := x(21, 16)
         *              // b := x(11,  0)
         *              x.sliceTo(a, b)
         * }}}
         */
        def sliceByteWiseTo(sinks: Bits*): Unit = {
            var lsb = 0
            sinks.reverse.foreach(y => {
                y match {
                    case s: SInt => s := x(lsb + s.getWidth - 1, lsb).asSInt
                    case u: UInt => u := x(lsb + u.getWidth - 1, lsb)
                    case _ => y := x(lsb + y.getWidth - 1, lsb)
                }
                lsb += y.getWidthUp8
            })
        }

        /**
         * Slice Bits into slices according to "up8" of specified widths
         *
         * @param widths Widths of each slices
         * @return Seq of slices
         */
        def slicesByteWise(widths: Int*): Seq[UInt] = {
            val res = widths.map(w => Wire(UInt(w.W)))
            sliceByteWiseTo(res: _*)
            res
        }

        /**
         * Extend multiple Bits to "up8" widths, concat, and connect them to this Bits
         *
         * @param sources The multiple bits to be concatenated
         */
        def catByteWiseFrom(sources: Bits*): Unit = {
            if (sources.isEmpty)
                x := 0.U
            else
                x := Cat(sources.map(s => s.extendToBytes))
        }

        /**
         * Reinterpret this Bits as a Fixed-Point with specified frac width
         * @param fw Width of fractional part
         * @return Fixed-Point Wire
         */
        def asFixPoint(fw: Int): FixPoint = {
            val res = Wire(FixPoint(x.getWidth.W, fw))
            res.bits := x.asSInt
            res
        }
    }

    implicit class UIntUtil(private val x: UInt) extends AnyVal {
        /**
         * Extend width by padding 0 to MSB side
         *
         * @param toBits Desired width of return data
         * @return Extended data
         */
        def extend(toBits: Int): UInt = {
            require(toBits >= x.getWidth)
            if (toBits > x.getWidth) {
                0.U((toBits - x.getWidth).W) ## x
            }
            else {
                x
            }
        }

        /**
         * Extend width to integer number of bytes
         *
         * @return Extended data
         */
        def extendToBytes: UInt = {
            x.extend(x.getWidthUp8)
        }

        /**
         * Repeat data bits, same as `Fill(n, data)` or `{n{data}}` in verilog
         *
         * @param n Times to repeat
         * @return Data with original data bits repeat n times in it.
         */
        def repeat(n: Int): UInt = {
            require(n >= 0)
            Fill(n, x)
        }

        def isPositive: Bool = {
            x =/= 0.U
        }

        def isNegative: Bool = false.B

        /**
         * @return maximum value this UInt can represents
         */
        def highestInBigInt: BigInt = {
            val w = x.getWidth
            val m = BigInt(1) << w
            m - 1
        }

        /**
         * @return minimum value this UInt can represents
         */
        def lowestInBigInt: BigInt = {
            BigInt(0)
        }

        /**
         * @return maximum value this UInt can present
         */
        def highest: UInt = {
            this.highestInBigInt.U(x.getWidth.W)
        }

        /**
         * @return minimum value this UInt can represents
         */
        def lowest: UInt = {
            this.lowestInBigInt.U(x.getWidth.W)
        }
    }

    implicit class SIntUtil(private val x: SInt) extends AnyVal {
        /**
         * Extend width by padding sign bit to MSB side
         *
         * @param toBits Desired width of return data
         * @return Extended data
         */
        def extend(toBits: Int): SInt = {
            require(toBits >= x.getWidth)
            if (toBits > x.getWidth)
                (Fill(toBits - x.getWidth, x.asUInt(x.getWidth - 1)) ## x.asUInt).asSInt
            else
                x
        }

        /**
         * Extend width to integer number of bytes
         *
         * @return Extended data
         */
        def extendToBytes: SInt = {
            x.extend(x.getWidthUp8)
        }

        def isPositive: Bool = {
            x > 0.S
        }

        def isNegative: Bool = {
            x(x.getWidth - 1) === 1.U
        }

        /**
         * @return Sign bit of this SInt
         */
        def signBit: Bool = x(x.getWidth - 1).asBool

        /**
         * @return maximum value this SInt can represents
         */
        def highestInBigInt: BigInt = {
            val w = x.getWidth
            val m = BigInt(1) << (w - 1)
            m - 1
        }

        /**
         * @return minimum value this SInt can represents
         */
        def lowestInBigInt: BigInt = {
            val w = x.getWidth
            val m = BigInt(1) << (w - 1)
            -m
        }

        /**
         * @return maximum value this SInt can present
         */
        def highest: SInt = {
            this.highestInBigInt.S(x.getWidth.W)
        }

        /**
         * @return minimum value this SInt can represents
         */
        def lowest: SInt = {
            this.lowestInBigInt.S(x.getWidth.W)
        }

        private def shiftLeft(n: Int): SInt = {
            val w: Int = x.getWidth
            (x(w - 1) ## (x(w - 2, 0) << n).take(w - 1)).asSInt
        }

        private def shiftRight(n: Int): SInt = {
            val w: Int = x.getWidth
            (x >> n).asSInt.extend(w)
        }

        private def shiftLeft(n: UInt): SInt = {
            val w: Int = x.getWidth
            (x(w - 1) ## (x(w - 2, 0) << n).take(w - 1)).asSInt
        }

        private def shiftRight(n: UInt): SInt = {
            val w: Int = x.getWidth
            (x >> n).asSInt.extend(w)
        }

        /**
         * Same behavior as it in Verilog/SystemVerilog when `n >= 0`,
         * and extends function when `n < 0` (eqv.: x >>> (-n) ).
         * @param n bits to be shifted.
         * @return shift result.
         * @note CAUTION: may cause value overflow.
         */
        final def <<<(n: Int): SInt = {
            if(n > 0) shiftLeft(n)
            else if(n < 0) shiftRight(-n)
            else x
        }

        /**
         * Same behavior as it in Verilog/SystemVerilog when `n >= 0`,
         * and extends function when `n < 0` (eqv.: x <<< (-n) ).
         * @param n bits to be shifted.
         * @return shift result.
         * @note CAUTION: may cause value overflow when `n < 0`.
         */
        final def >>>(n: Int): SInt = {
            if(n > 0) shiftRight(n)
            else if(n < 0) shiftLeft(-n)
            else x
        }

        /**
         * Same behavior as it in Verilog/SystemVerilog when `n >= 0`,
         * and extends function when `n < 0` (eqv.: x >>> (-n) ).
         * @param n bits to be shifted.
         * @return shift result.
         * @note CAUTION: may cause value overflow.
         */
        final def <<<(n: SInt): SInt = {
            MuxCase(x, Seq(
                (n > 0.S) -> shiftLeft(n.asUInt),
                (n < 0.S) -> shiftRight((-n).asUInt)
            ))
        }

        /**
         * Same behavior as it in Verilog/SystemVerilog when `n >= 0`,
         * and extends function when `n < 0` (eqv.: x >>> (-n) ).
         * @param n bits to be shifted.
         * @return shift result.
         * @note CAUTION: may cause value overflow when `n < 0`.
         */
        final def >>>(n: SInt): SInt = {
            MuxCase(x, Seq(
                (n > 0.S) -> shiftRight(n.asUInt),
                (n < 0.S) -> shiftLeft((-n).asUInt)
            ))
        }

        /**
         * Same behavior as it in Verilog/SystemVerilog.
         * @param n bits to be shifted.
         * @return shift result.
         * @note CAUTION: may cause value overflow.
         */
        final def <<<(n: UInt): SInt = shiftLeft(n)

        /**
         * Same behavior as it in Verilog/SystemVerilog.
         * @param n bits to be shifted.
         * @return shift result.
         */
        final def >>>(n: UInt): SInt = shiftRight(n)
    }

    /**
     * Extend each data to integer number of bytes, and then concat.
     *
     * @param x Seq of data
     * @return Concat result in UInt
     */
    def CatByteWise(x: Bits*): UInt = {
        if (x.isEmpty)
            0.U
        else
            Cat(x.map(u => u.extendToBytes))
    }

    /**
     * Extend each BigInt to integer number of bytes, and then concat.
     *
     * @param isSigned Seq of signedness of each data
     * @param w Seq of width of each data
     * @param x Seq of BigInts, lower w(i) bits of each is the actual data to
     *          be concat
     * @return Concat result in BigInt
     */
    def CatByteWise(isSigned: Seq[Boolean], w: Seq[Int], x: BigInt*): BigInt = {
        require(x.length == w.length)
        if (x.isEmpty)
            BigInt(0)
        else {
            var lsb = 0
            var res = BigInt(0)
            for (i <- x.length - 1 to 0 by -1) {
                val data = x(i)
                val width = w(i)
                if (isSigned(i))
                    res |= data.asSigned(width).asUnsigned(width.up8) << lsb
                else
                    res |= data.asUnsigned(width) << lsb
                lsb += width.up8
            }
            res.asUnsigned(lsb)
        }
    }

    implicit class BigIntUtils(private val x: BigInt) extends AnyVal {
        /**
         * @return The minimum number divisible by 8 and no less than this
         *         BigInt
         */
        def up8: BigInt = if (x < 0) 0 else ((x - 1) / 8 + 1) * 8

        /**
         * @return The number of high bit in this BigInt
         */
        def popCount: Int = {
            var n = 0
            for (i <- 0 until x.bitLength) {
                if (x.testBit(i))
                    n = n + 1
            }
            n
        }

        /**
         * @param mask Bits to be tested
         * @return True if `(x & mask) == mask`
         */
        def testBits(mask: BigInt): Boolean = {
            (x & mask) == mask
        }

        /**
         * @param mask Bits to be tested
         * @param bits Bits to be compared
         * @return True if `(x & mask) == bits`
         */
        def testBits(mask: BigInt, bits: BigInt): Boolean = {
            (x & mask) == bits
        }

        /**
         * @param nBit Number of lower bits to be reinterpreted
         * @return Reinterpret the lower nBit as unsigned
         */
        def asUnsigned(nBit: Int): BigInt = {
            x & ((BigInt(1) << nBit) - 1)
        }

        /**
         * @param nBit Number of lower bits to be reinterpreted
         * @return Reinterpret the lower nBit as signed
         */
        def asSigned(nBit: Int): BigInt = {
            if (x.testBits(BigInt(1) << (nBit - 1))) { // negative
                x.asUnsigned(nBit) - (BigInt(1) << nBit)
            }
            else { // positive
                x.asUnsigned(nBit)
            }
        }

        /**
         * Get bits by specifying MSB and LSB in this BigInt
         * @param msb Most significant bit pos
         * @param lsb Least significant bit pos
         * @return Bits in BigInt (this[msb : lsb])
         */
        def bitSlice(msb: Int, lsb: Int): BigInt = {
            (x >> lsb) & ((BigInt(2) << (msb - lsb)) - 1)
        }

        /**
         * Slice BigInt into slices according to widths
         *
         * @param widths Widths of each slices
         * @return Seq of slices
         */
        def bitSlices(widths: Int*): Seq[BigInt] = {
            var lsb = 0
            widths.reverse.map(w => {
                val res = x.bitSlice(lsb + w - 1, lsb)
                lsb += w
                res
            }).reverse
        }

        /**
         * Slice BigInt into slices according to "up8" of specified widths
         *
         * @param widths Widths of each slices
         * @return result slices
         */
        def bitSlicesByteWise(widths: Int*): Seq[BigInt] = {
            var lsb = 0
            widths.reverse.map(w => {
                val res = x.bitSlice(lsb + w - 1, lsb)
                lsb += w.up8
                res
            }).reverse
        }

        /**
         * Get the value if treat the lower width bits as a Fixed-Point with
         * specified frac width.
         *
         * @param width Width of effective bits
         * @param fracWidth Width of fractional part
         * @return Value of the Fixed-Point
         */
        def valueAsFixPoint(width: Int, fracWidth: Int): Double = {
            x.asSigned(width).toDouble * math.pow(2.0, -fracWidth)
        }

        /**
         * Get the value if treat the lower width bits as a Fixed-Point with
         * specified frac width.
         *
         * @param width Width of effective bits
         * @param fracWidth Width of fractional part
         * @return Value of the Fixed-Point
         * @note This BigDecimal version of `valueAsFixPoint` is more accurate
         *       than the Double version, when width or fracWidth is larger
         *       than or near to 64, this version is recommended.
         */
        def valueAsFixPointInBigDecimal(width: Int, fracWidth: Int): BigDecimal = {
            BigDecimal(x.asSigned(width)) * BigDecimal(2.0).pow(-fracWidth)
        }
    }

    implicit class LongUtils(private val x: Long) extends AnyVal {
        /**
         * @return The minimum number divisible by 8 and no less than this Long
         */
        def up8: Long = if (x < 0) 0 else ((x - 1) / 8 + 1) * 8

        /**
         * @param mask Bits to be tested
         * @return True if `(x & mask) == mask`
         */
        def testBits(mask: Long): Boolean = {
            (x & mask) == mask
        }

        /**
         * @param mask Bits to be tested
         * @param bits Bits to be compared
         * @return True if `(x & mask) == bits`
         */
        def testBits(mask: Long, bits: Long): Boolean = {
            (x & mask) == bits
        }

        /**
         * @param nBit Number of lower bits to be reinterpreted
         * @return Reinterpret the lower nBit as unsigned
         */
        def asUnsigned(nBit: Int): Long = {
            require(nBit < 64)
            x & ((1L << nBit) - 1)
        }

        /**
         * @param nBit Number of lower bits to be reinterpreted
         * @return Reinterpret the lower nBit as signed
         */
        def asSigned(nBit: Int): Long = {
            require(nBit <= 64)
            if (nBit == 64) {
                x
            }
            else if (x.testBits(1L << (nBit - 1))) { // negative
                x.asUnsigned(nBit) - (1L << nBit)
            }
            else { // positive
                x.asUnsigned(nBit)
            }
        }

        def toBigInt: BigInt = BigInt(x)
    }

    implicit class IntUtils(private val x: Int) extends AnyVal {
        /**
         * @return The minimum number divisible by 8 and no less than this Int
         */
        def up8: Int = if (x < 0) 0 else ((x - 1) / 8 + 1) * 8

        /**
         * @param mask Bits to be tested
         * @return True if `(x & mask) == mask`
         */
        def testBits(mask: Int): Boolean = {
            (x & mask) == mask
        }

        /**
         * @param mask Bits to be tested
         * @param bits Bits to be compared
         * @return True if `(x & mask) == bits`
         */
        def testBits(mask: Int, bits: Int): Boolean = {
            (x & mask) == bits
        }

        /**
         * @param nBit Number of lower bits to be reinterpreted
         * @return Reinterpret the lower nBit as unsigned
         */
        def asUnsigned(nBit: Int): Int = {
            require(nBit < 32)
            x & ((1 << nBit) - 1)
        }

        /**
         * @param nBit Number of lower bits to be reinterpreted
         * @return Reinterpret the lower nBit as signed
         */
        def asSigned(nBit: Int): Int = {
            require(nBit <= 32)
            if (nBit == 32) {
                x
            }
            else if (x.testBits(1 << (nBit - 1))) { // negative
                x.asUnsigned(nBit) - (1 << nBit)
            }
            else { // positive
                x.asUnsigned(nBit)
            }
        }

        def toBigInt: BigInt = BigInt(x)
    }

    implicit class ByteSeqReinterpret(private val bytes: Seq[Byte]) extends AnyVal {
        def asS8(idx: Int): Int = {
            require(idx + 1 <= bytes.length)
            bytes(idx).toInt
        }

        def asU8(idx: Int): Int = {
            require(idx + 1 <= bytes.length)
            bytes(idx) & 0xFF
        }

        def asS16(idx: Int, isBigEndian: Boolean = false): Int = {
            require(idx + 2 <= bytes.length)
            val x: Int = if (isBigEndian) {
                bytes.slice(idx, idx + 2).foldLeft(0)((acc, byte) => (acc << 8) | (byte & 0xFF))
            }
            else {
                bytes.slice(idx, idx + 2).foldRight(0)((byte, acc) => (byte & 0xFF) | (acc << 8))
            }
            x.asSigned(16)
        }

        def asU16(idx: Int, isBigEndian: Boolean = false): Int = {
            require(idx + 2 <= bytes.length)
            val x: Int = if (isBigEndian) {
                bytes.slice(idx, idx + 2).foldLeft(0)((acc, byte) => (acc << 8) | (byte & 0xFF))
            }
            else {
                bytes.slice(idx, idx + 2).foldRight(0)((byte, acc) => (byte & 0xFF) | (acc << 8))
            }
            x
        }

        def asS32(idx: Int, isBigEndian: Boolean = false): Int = {
            require(idx + 4 <= bytes.length)
            val x: Int = if (isBigEndian) {
                bytes.slice(idx, idx + 4).foldLeft(0)((acc, byte) => (acc << 8) | (byte & 0xFF))
            }
            else {
                bytes.slice(idx, idx + 4).foldRight(0)((byte, acc) => (byte & 0xFF) | (acc << 8))
            }
            x
        }

        def asU32(idx: Int, isBigEndian: Boolean = false): Long = {
            require(idx + 4 <= bytes.length)
            val x: Long = if (isBigEndian) {
                bytes.slice(idx, idx + 4).foldLeft(0L)((acc, byte) => (acc << 8) | (byte & 0xFF))
            }
            else {
                bytes.slice(idx, idx + 4).foldRight(0L)((byte, acc) => (byte & 0xFF) | (acc << 8))
            }
            x.asUnsigned(32)
        }

        def asS64(idx: Int, isBigEndian: Boolean = false): Long = {
            require(idx + 8 <= bytes.length)
            val x: Long = if (isBigEndian) {
                bytes.slice(idx, idx + 8).foldLeft(0L)((acc, byte) => (acc << 8) | (byte & 0xFF))
            }
            else {
                bytes.slice(idx, idx + 8).foldRight(0L)((byte, acc) => (byte & 0xFF) | (acc << 8))
            }
            x
        }

        def asU64(idx: Int, isBigEndian: Boolean = false): BigInt = {
            require(idx + 8 <= bytes.length)
            val x: BigInt = if (isBigEndian) {
                bytes.slice(idx, idx + 8).foldLeft(BigInt(0))((acc, byte) => (acc << 8) | (byte & 0xFF))
            }
            else {
                bytes.slice(idx, idx + 8).foldRight(BigInt(0))((byte, acc) => (byte & 0xFF) | (acc << 8))
            }
            x.asUnsigned(64)
        }

        def asUInt(idx: Int, nBit: Int, isBigEndian: Boolean = false): BigInt = {
            require(nBit > 0, s"In Seq[Byte].asUInt(), nBit must be > 0, got ${nBit}.")
            val nBytes = (nBit - 1) / 8 + 1
            require(idx + nBytes <= bytes.length)
            val x: BigInt = if (isBigEndian) {
                bytes.slice(idx, idx + nBytes).foldLeft(BigInt(0))((acc, byte) => (acc << 8) | (byte & 0xFF))
            }
            else {
                bytes.slice(idx, idx + nBytes).foldRight(BigInt(0))((byte, acc) => (byte & 0xFF) | (acc << 8))
            }
            x.asUnsigned(nBit)
        }

        def asSInt(idx: Int, nBit: Int, isBigEndian: Boolean = false): BigInt = {
            require(nBit > 0, s"In Seq[Byte].asInt(), nBit must be > 0, got ${nBit}.")
            val nBytes = (nBit - 1) / 8 + 1
            require(idx + nBytes <= bytes.length)
            val y: BigInt = if (isBigEndian) {
                bytes.slice(idx, idx + nBytes).foldLeft(BigInt(0))((acc, byte) => (acc << 8) | (byte & 0xFF))
            }
            else {
                bytes.slice(idx, idx + nBytes).foldRight(BigInt(0))((byte, acc) => (byte & 0xFF) | (acc << 8))
            }
            y.asSigned(nBit)
        }
    }

    implicit class StrBitsUtils[+T <: StrBits](private val x: T) extends AnyVal {
        def hasData: Boolean = x.isInstanceOf[StrHasData]

        def hasStrb: Boolean = x.isInstanceOf[StrHasStrb]

        def hasKeep: Boolean = x.isInstanceOf[StrHasKeep]

        def hasLast: Boolean = x.isInstanceOf[StrHasLast]

        def hasId: Boolean = x.isInstanceOf[StrHasId]

        def hasDest: Boolean = x.isInstanceOf[StrHasDest]

        def hasUser: Boolean = x.isInstanceOf[StrHasUser]

        def dataWidth: Int = {
            x match {
                case y: StrHasData => y.data.getWidth
                case _ => 0
            }
        }

        def pokeDataIfExist(data: BigInt): Option[BigInt] = {
            x match {
                case y: StrHasData => {
                    y.data match {
                        case d: UInt => d.poke(data)
                        case d: SInt => d.poke(data)
                        case _ => y.data.poke(data.asUInt)
                    }
                    Some(data)
                }
                case _ => None
            }
        }

        def peekDataIfExist: Option[BigInt] = {
            x match {
                case y: StrHasData => {
                    y.data match {
                        case d: UInt => Some(d.peekInt())
                        case d: SInt => Some(d.peekInt())
                        case _ => Some(y.data.asUInt.peekInt())
                    }
                }
                case _ => None
            }
        }

        def dataBytesCeil: Int = {
            x match {
                case y: StrHasData => (y.data.getWidth - 1) / 8 + 1
                case _ => 0
            }
        }

        def bytesPerBeat: Int = {
            x match {
                case y: StrHasData => (y.data.getWidth - 1) / 8 + 1
                case _ => 0
            }
        }

        def strbWidth: Int = {
            x match {
                case y: StrHasStrb => y.strb.getWidth
                case _ => 0
            }
        }

        def strbMask: BigInt = {
            (BigInt(1) << dataBytesCeil) - 1
        }

        def pokeStrbIfExist(strb: BigInt): Option[BigInt] = {
            x match {
                case y: StrHasStrb => {
                    y.strb.poke(strb)
                    Some(strb)
                }
                case _ => None
            }
        }

        def peekStrbIfExist: Option[BigInt] = {
            x match {
                case y: StrHasStrb => Some(y.strb.peekInt())
                case _ => None
            }
        }

        def peekStrbImplicit: BigInt = {
            x match {
                case y: StrHasStrb => y.strb.peekInt()
                case _ => strbMask
            }
        }

        def keepWidth: Int = {
            x match {
                case y: StrHasKeep => y.keep.getWidth
                case _ => 0
            }
        }

        def keepMask: BigInt = {
            (BigInt(1) << dataBytesCeil) - 1
        }

        def pokeKeepIfExist(keep: BigInt): Option[BigInt] = {
            x match {
                case y: StrHasKeep => {
                    y.keep.poke(keep)
                    Some(keep)
                }
                case _ => None
            }
        }

        def peekKeepIfExist: Option[BigInt] = {
            x match {
                case y: StrHasKeep => Some(y.keep.peekInt())
                case _ => None
            }
        }

        def peekKeepImplicit: BigInt = {
            x match {
                case y: StrHasKeep => y.keep.peekInt()
                case _ => keepMask
            }
        }

        def pokeLastIfExist(last: Boolean): Option[Boolean] = {
            x match {
                case y: StrHasLast => {
                    y.last.poke(last)
                    Some(last)
                }
                case _ => None
            }
        }

        def peekLastIfExist: Option[Boolean] = {
            x match {
                case y: StrHasLast => Some(y.last.peekBoolean())
                case _ => None
            }
        }

        def idWidth: Int = {
            x match {
                case y: StrHasId => y.id.getWidth
                case _ => 0
            }
        }

        def pokeIdIfExist(id: Long): Option[Long] = {
            x match {
                case y: StrHasId => {
                    y.id.poke(BigInt(id))
                    Some(id)
                }
                case _ => None
            }
        }

        def peekIdIfExist: Option[Long] = {
            x match {
                case y: StrHasId => Some(y.id.peekInt().toLong)
                case _ => None
            }
        }

        def destWidth: Int = {
            x match {
                case y: StrHasDest => y.dest.getWidth
                case _ => 0
            }
        }

        def pokeDestIfExist(dest: Long): Option[Long] = {
            x match {
                case y: StrHasDest => {
                    y.dest.poke(BigInt(dest))
                    Some(dest)
                }
                case _ => None
            }
        }

        def peekDestIfExist: Option[Long] = {
            x match {
                case y: StrHasDest => Some(y.dest.peekInt().toLong)
                case _ => None
            }
        }

        def userWidth: Int = {
            x match {
                case y: StrHasUser => y.user.getWidth
                case _ => 0
            }
        }

        def userBitsPerDataByte: Int = {
            require(x.isInstanceOf[StrHasData])
            val bpb = (x.asInstanceOf[StrHasData].data.getWidth - 1) / 8 + 1
            x match {
                case y: StrHasUser => (y.user.getWidth - 1) / bpb + 1
                case _ => 0
            }
        }

        def pokeUserIfExist(user: BigInt): Option[BigInt] = {
            x match {
                case y: StrHasUser => {
                    y.user match {
                        case u: UInt => u.poke(user)
                        case u: SInt => u.poke(user)
                        case _ => y.user.poke(user.asUInt)
                    }
                    // y.user.poke(user)
                    Some(user)
                }
                case _ => None
            }
        }

        def peekUserIfExist: Option[BigInt] = {
            x match {
                case y: StrHasUser => {
                    y.user match {
                        case u: UInt => Some(u.peekInt())
                        case u: SInt => Some(u.peekInt())
                        case _ => Some(y.user.asUInt.peekInt())
                    }
                    // Some(y.user.peekInt())
                }
                case _ => None
            }
        }
    }

    /**
     * Chisel type or hardware of a FixPoint
     * @param bits Raw bits of FixPoint
     * @param fw Width of fractional part
     * @note
     *  - Chisel type if bits is a chisel type (e.g.: SInt(16.W))
     *  - Chisel hardware if bits is a chisel hardware (e.g.: 123.S(16.W))
     */
    sealed class FixPoint(val bits: SInt, private val fw: Int) extends Bundle {
        require(0 <= fw && fw < 128, s"In FixPoint, fw must be >= 0 and < 128, but fw=${fw} got.")
        require(bits.widthKnown)
        require(!bits.isLit)

        /**
         * @return total width
         */
        def getTotalWidth: Int = this.bits.getWidth

        /**
         * @return width of integer part (including sign bit).
         */
        def getIntWidth: Int = this.bits.getWidth - fw

        /**
         * @return width of fractional part.
         */
        def getFracWidth: Int = fw

        /**
         * @return Zero.
         */
        def zero: FixPoint = FixPoint.zero(bits.getWidth, fw)

        /**
         * @return Negative (0.0 - this).
         * @note width of result will be original width + 1.
         */
        def negative: FixPoint = FixPoint(0.S -& bits, fw)

        /**
         * @return Lowest FixPoint (negative with max abs).
         */
        def lowest: FixPoint = FixPoint.lowest(bits.getWidth, fw)

        /**
         * @return Highest FixPoint (positive with max abs).
         */
        def highest: FixPoint = FixPoint.highest(bits.getWidth, fw)

        /**
         * @return Epsilon FixPoint (positive with min abs).
         */
        def epsilon: FixPoint = FixPoint.epsilon(bits.getWidth, fw)

        /**
         * @return True if this is the lowest value (negative with max abs).
         */
        def isLowest: Bool = this.bits.asUInt === (1.U(1.W) ## 0.U((bits.getWidth - 1).W))

        /**
         * @return True if this is the highest value (positive with max abs).
         */
        def isHighest: Bool = this.bits.asUInt === (0.U(1.W) ## Fill(bits.getWidth - 1, 1.U(1.W)))

        /**
         * @return True if this is the epsilon value (positive with min abs).
         */
        def isEpsilon: Bool = this.bits.asUInt === (1.U(bits.getWidth.W))

        /**
         * Set this to the lowest value (negative with max abs).
         */
        def setToLowest: Unit = this.bits := (1.U(1.W) ## 0.U((bits.getWidth - 1).W)).asSInt

        /**
         * Set this to the highest value (positive with max abs).
         */
        def setToHighest: Unit = this.bits := (0.U(1.W) ## Fill(bits.getWidth - 1, 1.U(1.W))).asSInt

        /**
         * Set this to the epsilon value (positive with min abs).
         */
        def setToEpsilon: Unit = this.bits := 1.U(bits.getWidth.W).asSInt

        /**
         * @param value the value to be represented.
         */
        def setTo(value: Double): Unit = {
            require(inRangeAfterRound(value), s"In FixPoint.setTo, ${value} can not fit in fix point binary with width=${bits.getWidth} and fw=${fw}.")
            // this.bits := (value * math.pow(2.0, fw)).round.asSInt
            this.bits := FixPoint.round(value * math.pow(2.0, fw)).asSInt
        }

        /**
         * @param value the value to be represented.
         */
        def setTo(value: BigDecimal): Unit = {
            require(inRangeAfterRound(value))
            this.bits := FixPoint.round(value * BigDecimal(2.0).pow(fw)).asSInt
        }

        /**
         * @return Lowest value (negative with max abs) in Double.
         */
        def lowestInDouble: Double = FixPoint.lowestInDouble(bits.getWidth, fw)

        /**
         * @return Highest value (positive with max abs) in Double.
         */
        def highestInDouble: Double = FixPoint.highestInDouble(bits.getWidth, fw)

        /**
         * @return Epsilon value (positive with min abs) in Double.
         */
        def epsilonInDouble: Double = FixPoint.epsilonInDouble(fw)

        /**
         * @return Lowest value (negative with max abs) in BigDecimal.
         */
        def lowestInBigDecimal: BigDecimal = FixPoint.lowestInBigDecimal(bits.getWidth, fw)

        /**
         * @return Highest value (positive with max abs) in BigDecimal.
         */
        def highestInBigDecimal: BigDecimal = FixPoint.highestInBigDecimal(bits.getWidth, fw)

        /**
         * @return Epsilon value (positive with min abs) in BigDecimal.
         */
        def epsilonInBigDecimal: BigDecimal = FixPoint.epsilonInBigDecimal(fw)

        /**
         * Test whether the value can be fit into this FixPoint.
         * @param value The value to be tested.
         * @return True if the value can be fit into this FixPoint.
         */
        def inRange(value: Double): Boolean = FixPoint.inRange(bits.getWidth, fw, value)

        /**
         *  Test whether the value can be fit into this FixPoint after rounded to nearest quantitative step.
         * @param value The value to be tested.
         * @return True if the rounded value can be fit into this FixPoint.
         * @note The round mode is specified in FixPoint.roundMode.
         */
        def inRangeAfterRound(value: Double): Boolean = FixPoint.inRangeAfterRound(bits.getWidth, fw, value)

        /**
         * Test whether the value can be fit into this FixPoint.
         * @param value The value to be test.
         * @return True if the value can be fit into this FixPoint.
         */
        def inRange(value: BigDecimal): Boolean = FixPoint.inRange(bits.getWidth, fw, value)

        /**
         *  Test whether the value can be fit into this FixPoint after rounded to nearest quantitative step.
         * @param value The value to be tested.
         * @return True if the rounded value can be fit into this FixPoint.
         * @note The round mode is specified in FixPoint.roundMode.
         */
        def inRangeAfterRound(value: BigDecimal): Boolean = FixPoint.inRangeAfterRound(bits.getWidth, fw, value)

        /**
         * @param value assign a double constant/parameter
         * @note due to the characteristics of Fixpoint, value will be quantified (loss precision)
         */
        final def :=(value: Double): Unit = {
            require(inRangeAfterRound(value), s"In FixPoint, ${value} can not fit in fix point binary with width=${bits.getWidth} and fw=${fw}.")
            // this.bits := (value * math.pow(2.0, fw)).round.asSInt
            this.bits := FixPoint.round(value * math.pow(2.0, fw)).asSInt
        }

        /**
         * @param value assign a BigDecimal constant/parameter
         * @note due to the characteristics of Fixpoint, value will be quantified (loss precision)
         */
        final def :=(value: BigDecimal): Unit = {
            require(inRangeAfterRound(value), s"In FixPoint, ${value} can not fit in fix point binary with width=${bits.getWidth} and fw=${fw}.")
            // this.bits := (value * BigDecimal(2.0).pow(fw)).setScale(0, BigDecimal.RoundingMode.HALF_UP).toBigInt.asSInt
            this.bits := FixPoint.round(value * BigDecimal(2.0).pow(fw)).asSInt
        }

        /**
         * @param sint assign a SInt to integer part and clear fractional part.
         */
        final def :=(sint: SInt): Unit = {
            this.bits := (sint << fw)
        }

        /**
         * @param that assign another FixPoint
         * @note
         *  - if fw of destination is larger, zeros will be fill in lsb.
         *  - if fw of destination is smaller, tail bits in source will be discarded (loss precision).
         *  - if iw of destination is larger, sign bit will be fill in msb.
         *  - if iw of destination is smaller, msb will be discarded (overflow).
         */
        final def :=(that: FixPoint): Unit = {
            val bits = {
                if (this.fw > that.fw) (that.bits << (this.fw - that.fw)).asSInt
                else if (this.fw < that.fw) (that.bits >> (that.fw - this.fw)).asSInt
                else that.bits
            }
            this.bits := bits
        }

        private def matchPoint(that: FixPoint): (SInt, SInt, Int) = {
            val fw = math.max(this.fw, that.fw)
            val lBits = (this.bits << (fw - this.fw)).asSInt
            val rBits = (that.bits << (fw - that.fw)).asSInt
            (lBits, rBits, fw)
        }

        /**
         * @return Sum of 2 FixPoint
         * @note
         *  - IW of result = max{opd1.IW, opd2.IW} + 1
         *  - FW of result = max{opd1.FW, opd2.FW}
         */
        final def +(that: FixPoint): FixPoint = {
            val (l, r, fw) = matchPoint(that)
            val bits: SInt = l +& r
            new FixPoint(bits, fw)
        }

        /**
         * @return Subs of 2 FixPoint
         * @note
         *  - IW of result = max{opd1.IW, opd2.IW} + 1
         *  - FW of result = max{opd1.FW, opd2.FW}
         */
        final def -(that: FixPoint): FixPoint = {
            val (l, r, fw) = matchPoint(that)
            val bits: SInt = l -& r
            new FixPoint(bits, fw)
        }

        /**
         * @return Productor of 2 FixPoint
         * @note
         *  - IW of result = opd1.IW + opd2.IW
         *  - FW of result = opd1.FW + opd2.FW
         */
        final def *(that: FixPoint): FixPoint = {
            val fw = this.fw + that.fw
            val bits: SInt = this.bits * that.bits
            new FixPoint(bits, fw)
        }

        /**
         * @return True if 2 opd equals exactly
         */
        final def ===(that: FixPoint): Bool = {
            val (l, r, _) = matchPoint(that)
            Mux(l === r, true.B, false.B)
        }

        /**
         * @return True if 2 opd NOT exactly equals
         */
        final def =/=(that: FixPoint): Bool = {
            val (l, r, _) = matchPoint(that)
            Mux(l =/= r, true.B, false.B)
        }

        /**
         * @return True if left opd is less than right opd
         */
        final def <(that: FixPoint): Bool = {
            val (l, r, _) = matchPoint(that)
            Mux(l < r, true.B, false.B)
        }

        /**
         * @return True if left opd is less than or equal to right opd
         */
        final def <=(that: FixPoint): Bool = {
            val (l, r, _) = matchPoint(that)
            Mux(l <= r, true.B, false.B)
        }

        /**
         * @return True if left opd is larger than or equal to right opd
         */
        final def >=(that: FixPoint): Bool = {
            val (l, r, _) = matchPoint(that)
            Mux(l >= r, true.B, false.B)
        }

        /**
         * @return True if left opd is larger than to right opd
         */
        final def >(that: FixPoint): Bool = {
            val (l, r, _) = matchPoint(that)
            Mux(l > r, true.B, false.B)
        }

        private def shiftLeft(n: Int): FixPoint = {
            val w: Int = this.bits.getWidth
            (bits(w - 1) ## (bits(w - 2, 0) << n).take(w - 1)).asFixPoint(fw)
        }

        private def shiftRight(n: Int): FixPoint = {
            val w: Int = this.bits.getWidth
            (bits >> n).asSInt.extend(w).asFixPoint(fw)
        }

        private def shiftLeft(n: UInt): FixPoint = {
            val w: Int = this.bits.getWidth
            (bits(w - 1) ## (bits(w - 2, 0) << n).take(w - 1)).asFixPoint(fw)
        }

        private def shiftRight(n: UInt): FixPoint = {
            val w: Int = this.bits.getWidth
            (bits >> n).asSInt.extend(w).asFixPoint(fw)
        }

        /**
         * Keep sign bit and shift other bits, eqv. to x * 2**n.
         * @param n bits to be shifted, n can be negative.
         * @return shift result.
         * @note CAUTION: may cause value overflow.
         */
        final def <<<(n: Int): FixPoint = {
            if(n > 0)
                shiftLeft(n)
            else if(n < 0)
                shiftRight(-n)
            else
                this
        }

        /**
         * Keep sign bit and shift other bits, eqv. to x / 2**n.
         * @param n bits to be shifted, n can be negative.
         * @return shift result.
         * @note CAUTION: may cause value overflow when `n < 0`.
         */
        final def >>>(n: Int): FixPoint = {
            if(n > 0)
                shiftRight(n)
            else if(n < 0)
                shiftLeft(-n)
            else
                this
        }

        /**
         * Keep sign bit and shift other bits, eqv. to x * 2**n.
         * @param n bits to be shifted, n can be negative.
         * @return shift result.
         * @note CAUTION: may cause value overflow.
         */
        final def <<<(n: SInt): FixPoint = {
            MuxCase(this, Seq(
                (n > 0.S) -> shiftLeft(n.asUInt),
                (n < 0.S) -> shiftRight((-n).asUInt)
            ))
        }

        /**
         * Keep sign bit and shift other bits, eqv. to x / 2**n.
         * @param n bits to be shifted, n can be negative.
         * @return shift result.
         * @note CAUTION: may cause value overflow when `n < 0`.
         */
        final def >>>(n: SInt): FixPoint = {
            MuxCase(this, Seq(
                (n > 0.S) -> shiftRight(n.asUInt),
                (n < 0.S) -> shiftLeft((-n).asUInt)
            ))
        }

        /**
         * Keep sign bit and shift other bits, eqv. to x * 2**n.
         * @param n bits to be shifted.
         * @return shift result.
         */

        /**
         * Keep sign bit and shift other bits, eqv. to x / 2**n.
         * @param n bits to be shifted.
         * @return shift result.
         */
        final def >>>(n: UInt): FixPoint = shiftRight(n)

        /**
         * Truncate
         *
         * @param newFracWidth Width of fractional part of result
         * @return Truncated result
         * @note same as Floor()
         */
        def truncate(newFracWidth: Int = 0): FixPoint = {
            require(0 <= newFracWidth && newFracWidth < fw)
            val shift = fw - newFracWidth
            new FixPoint((this.bits >> shift).asSInt, newFracWidth)
        }

        /**
         * Floor
         *
         * @param newFracWidth Width of fractional part of result
         * @return Floor result
         * @note
         *      - Fraction width of result = newFracWidth
         *      - Integer width of result = original integer width
         */
        def floor(newFracWidth: Int = 0): FixPoint = {
            require(0 <= newFracWidth && newFracWidth < fw)
            val shift = fw - newFracWidth
            new FixPoint((this.bits >> shift).asSInt, newFracWidth)
        }

        /**
         * Ceil
         *
         * @param newFracWidth Width of fractional part of result
         * @return Ceil result
         * @note
         *      - Fraction width of result = newFracWidth
         *      - Integer width of result = original integer width + 1
         */
        def ceil(newFracWidth: Int = 0): FixPoint = {
            require(0 <= newFracWidth && newFracWidth < fw)
            val shift = fw - newFracWidth
            val bits = 0.S -& this.bits
            new FixPoint(-(bits >>> shift).asSInt, newFracWidth)
        }

        /**
         * Truncate by rounding (half ceiling, rounding to +inf)
         *
         * @param newFracWidth Width of fractional part of result
         * @return Truncated result
         * @note
         *  - this "round" is actually half ceiling (round half to +inf).
         *  - due to implementation complexity, this is the most economy
         *    rounding type, and other rounding types are not implemented.
         */
        def round(newFracWidth: Int = 0): FixPoint = {
            require(newFracWidth < fw)
            val shift = fw - newFracWidth
            val bits = Mux(
                this.bits(shift - 1),
                1.S + (this.bits >> shift).asSInt,
                (this.bits >> shift).asSInt
            )
            new FixPoint(bits, newFracWidth)
        }

        /**
         * @return Raw bits of fractional part
         */
        def getFractionBits: UInt = {
            this.bits(this.getFracWidth - 1, 0).asUInt
        }

        /**
         * @return FixPoint represents only pure fractional part (clear integer part).
         * @note
         *     - fractional width = original fractional width
         *     - integer width = 1
         */
        def getPureFraction: FixPoint = {
            (0.U(1.W) ## this.getFractionBits).asFixPoint(this.getFracWidth)
        }

        /**
         * @return Raw bits of this FixPoint
         */
        def asSInt: SInt = this.bits

        /**
         * @param fw new fractional part width
         * @return Reinterpret as a FixPoint with new fractional part width
         */
        def asFixPoint(fw: Int): FixPoint = {
            new FixPoint(bits, fw)
        }

        /**
         * @return Raw bits of this FixPoint
         */
        def getBits: SInt = this.bits

        /**
         * @return Convert to SInt by truncate fractional part
         * @note Same as floorToSInt
         */
        def toSInt: SInt = this.truncate().bits

        /**
         * @return Maximum SInt not larger than this FixPoint
         */
        def floorToSInt: SInt = this.floor().bits

        /**
         * @return Minimum SInt not smaller than this FixPoint
         */
        def ceilToSInt: SInt = this.ceil().bits

        /**
         * @return Nearest SInt to this FixPoint
         * @note
         *  - this "round" is actually half ceiling (round half to +inf).
         *  - due to implementation complexity, this is the most economy
         *    rounding type, and other rounding types are not implemented yet.
         */
        def roundToSInt: SInt = this.round().bits

        /**
         * @param fw new fractional part width
         * @return   Convert to new FixPoint with specified fractional part width
         */
        def toFixPoint(fw: Int): FixPoint = {
            val bits = {
                if(fw > this.fw) this.bits << (fw - this.fw)
                else if(fw < this.fw) this.bits >> (this.fw - fw)
                else this.bits
            }
            new FixPoint(bits.asSInt, fw)
        }

        /**
         * @return Peek value as Double
         */
        def peekDouble(): Double = {
            val d = this.bits.peekInt()
            d.toDouble * math.pow(2.0, -fw)
        }

        /**
         * @return Peek value as BigDecimal
         */
        def peekBigDecimal(): BigDecimal = {
            val d = this.bits.peekInt()
            BigDecimal(d) * BigDecimal(2.0).pow(-fw)
        }
    }

    object FixPoint {

        /**
         * RoundMode.Up: round half away from 0.
         * RoundMode.Down: round half near to 0.
         * RoundMode.Even: round half to even number.
         * RoundMode.Ceil: round half to +inf.
         * RoundMode.Floor: round half to -inf.
         */
        object RoundMode extends Enumeration {
            val Up, Down, Even, Ceil, Floor = Value
        }

        /**
         * Change this to specified rounding behavior for quantifying Double or
         * BigDecimal value to FixPoint.
         */
        val roundMode: RoundMode.Value = RoundMode.Even

        def round(x: Double): Long = {
            roundMode match {
                case RoundMode.Up => x.halfUp
                case RoundMode.Down => x.halfDown
                case RoundMode.Even => x.halfEven
                case RoundMode.Ceil => x.halfCeil
                case RoundMode.Floor => x.halfFloor
            }
        }

        def round(x: BigDecimal): BigInt = {
            roundMode match {
                case RoundMode.Up => x.halfUp
                case RoundMode.Down => x.halfDown
                case RoundMode.Even => x.halfEven
                case RoundMode.Ceil => x.halfCeil
                case RoundMode.Floor => x.halfFloor
            }
        }

        /**
         * Create a Chisel type of FixPoint
         *
         * @param width total width
         * @param fw    fraction width (or binary point position)
         * @return a Chisel type
         */
        def apply(width: Width, fw: Int): FixPoint = {
            new FixPoint(SInt(width), fw)
        }

        /**
         * Create a hardware of FixPoint
         *
         * @param bits Raw bits for Fixpoint hardware, width of bits is the
         *             total width of FixPoint
         * @param fw   fraction width (or binary point position)
         * @return a hardware of FixPoint
         */
        def apply(bits: SInt, fw: Int): FixPoint = {
            val res = Wire(FixPoint(bits.getWidth.W, fw))
            res.bits := bits
            res
        }

        /**
         * Create a hardware of FixPoint
         *
         * @param width total width
         * @param fw    fraction width (or binary point position)
         * @param value double value to be represent
         * @return the FixPoint hardware created
         */
        def apply(width: Width, fw: Int, value: Double): FixPoint = {
            val res = Wire(FixPoint(width, fw))
            res := value
            res
        }

        /**
         * Create a Chisel type of FixPoint by specified possible lowest value
         * instead of width.
         *
         * @param fw Width of fractional part.
         * @param possibleLowest Possible lowest value may be presented by the
         *                       returned FixPoint.
         * @return Chisel type of FixPoint.
         */
        def byLowest(fw: Int, possibleLowest: Double): FixPoint = {
            require(fw >= 0)
            require(possibleLowest <= -1.0)
            // val m = (-possibleLowest * math.pow(2.0, fw)).round
            val m = FixPoint.round(-possibleLowest * math.pow(2.0, fw))
            val width = log2Up(m) + 1
            new FixPoint(SInt(width.W), fw)
        }

        /**
         * Create a Chisel type of FixPoint by specified possible highest value
         * instead of width.
         *
         * @param fw Width of fractional part.
         * @param possibleHighest Possible highest value may be presented by the
         *                       returned FixPoint.
         * @return Chisel type of FixPoint.
         */
        def byHighest(fw: Int, possibleHighest: Double): FixPoint = {
            require(fw >= 0)
            require(possibleHighest >= 0.0)
            // val m = 1 + (possibleHighest * math.pow(2.0, fw)).round
            val m = 1 + FixPoint.round(possibleHighest * math.pow(2.0, fw))
            val width = log2Up(m) + 1
            new FixPoint(SInt(width.W), fw)
        }

        /**
         * Create a Chisel type of FixPoint by specified value range instead
         * of width.
         *
         * @param fw Width of fractional part.
         * @param lowest Possible lowest value may be presented by the returned
         *               FixPoint.
         * @param highest Possible highest value may be presented by the
         *                returned Fixpoint.
         * @return Chisel type of FixPoint.
         */
        def byRange(fw: Int, lowest: Double, highest: Double): FixPoint = {
            require(fw >= 0)
            require(lowest <= -1.0 && highest >= 0.0)
            val epsilon = math.pow(0.5, fw)
            val upLimit =
                if(-lowest > highest + epsilon)
                    -lowest
                else
                    highest + epsilon
            // val m = (upLimit * math.pow(2.0, fw)).round
            val m = FixPoint.round(upLimit * math.pow(2.0, fw))
            val width = log2Up(m) + 1
            new FixPoint(SInt(width.W), fw)
        }

        /**
         * Create a Chisel type of FixPoint by specified possible lowest value
         * instead of width.
         *
         * @param possibleLowest Possible lowest value may be presented by the
         *                       returned FixPoint.
         * @param fw Width of fractional part.
         * @return Chisel type of FixPoint.
         */
        def byLowest(fw: Int, possibleLowest: BigDecimal): FixPoint = {
            require(fw >= 0)
            require(possibleLowest <= -1.0)
            // val m = (-possibleLowest * BigDecimal(2.0).pow(fw)).setScale(0, BigDecimal.RoundingMode.HALF_UP).toBigInt
            val m = FixPoint.round(-possibleLowest * BigDecimal(2.0).pow(fw))
            val width = log2Up(m) + 1
            new FixPoint(SInt(width.W), fw)
        }

        /**
         * Create a Chisel type of FixPoint by specified possible highest value
         * instead of width.
         *
         * @param possibleHighest Possible highest value may be presented by the
         *                       returned FixPoint.
         * @param fw Width of fractional part.
         * @return Chisel type of FixPoint.
         */
        def byHighest(fw: Int, possibleHighest: BigDecimal): FixPoint = {
            require(fw >= 0)
            require(possibleHighest >= 0.0)
            // val m = 1 + (possibleHighest * BigDecimal(2.0).pow(fw)).setScale(0, BigDecimal.RoundingMode.HALF_UP).toBigInt
            val m = 1 + FixPoint.round(possibleHighest * BigDecimal(2.0).pow(fw))
            val width = log2Up(m) + 1
            new FixPoint(SInt(width.W), fw)
        }

        /**
         * Create a Chisel type of FixPoint by specified value range instead
         * of width.
         *
         * @param fw Width of fractional part.
         * @param lowest Possible lowest value may be presented by the returned
         *               FixPoint.
         * @param highest Possible highest value may be presented by the
         *                returned Fixpoint.
         * @return Chisel type of FixPoint.
         */
        def byRange(fw: Int, lowest: BigDecimal, highest: BigDecimal): FixPoint = {
            require(fw >= 0)
            require(lowest <= -1.0 && highest >= 0.0)
            val epsilon = BigDecimal(0.5).pow(fw)
            val upLimit =
                if(-lowest > highest + epsilon)
                    -lowest
                else
                    highest + epsilon
            // val m = (upLimit * BigDecimal(2.0).pow(fw)).setScale(0, BigDecimal.RoundingMode.HALF_UP).toBigInt
            val m = FixPoint.round(upLimit * BigDecimal(2.0).pow(fw))
            val width = log2Up(m) + 1
            new FixPoint(SInt(width.W), fw)
        }

        def zero(w: Int, fw: Int): FixPoint = {
            require(w >= 1 && fw >= 0)
            0.S(w.W).asFixPoint(fw)
        }

        /**
         * @param w Total width
         * @param fw Fractional part width
         * @return Lowest (negative with max abs) FixPoint.
         */
        def lowest(w: Int, fw: Int): FixPoint = {
            require(w >= 1 && fw >= 0)
            (1.U(1.W) ## 0.U((w - 1).W)).asFixPoint(fw)
        }

        /**
         * @param w Total width
         * @param fw Fractional part width
         * @return Highest (positive with max abs) FixPoint.
         */
        def highest(w: Int, fw: Int): FixPoint = {
            require(w >= 1 && fw >= 0)
            (0.U(1.W) ## Fill(w - 1, 1.U(1.W))).asFixPoint(fw)
        }

        /**
         * @param w Total width
         * @param fw Fractional part width
         * @return Epsilon (positive with min abs) FixPoint.
         */
        def epsilon(w: Int, fw: Int): FixPoint = {
            require(w >= 1 && fw >= 0)
            (1.S(w.W)).asFixPoint(fw)
        }

        /**
         * @param w Total width
         * @param fw Fractional part width
         * @return Lowest (negative with max abs) FixPoint value in Double.
         */
        def lowestInDouble(w: Int, fw: Int): Double = {
            require(w >= 1 && fw >= 0)
            -math.pow(2.0, w - 1 - fw)
        }

        /**
         * @param w Total width
         * @param fw Fractional part width
         * @return Highest (positive with max abs) FixPoint value in Double.
         */
        def highestInDouble(w: Int, fw: Int): Double = {
            require(w >= 1 && fw >= 0)
            math.pow(2.0, w - 1 - fw) - math.pow(2.0, -fw)
        }

        /**
         * @param fw Fractional part width
         * @return psilon (positive with min abs) FixPoint value in Double.
         */
        def epsilonInDouble(fw: Int): Double = {
            require(fw >= 0)
            math.pow(2.0, -fw)
        }

        /**
         * @param w Total width
         * @param fw Fractional part width
         * @return Lowest (negative with max abs) FixPoint value in BigDecimal.
         */
        def lowestInBigDecimal(w: Int, fw: Int): BigDecimal = {
            -BigDecimal(2.0).pow(w - 1 - fw)
        }

        /**
         * @param w Total width
         * @param fw Fractional part width
         * @return Highest (positive with max abs) FixPoint value in BigDecimal.
         */
        def highestInBigDecimal(w: Int, fw: Int): BigDecimal = {
            BigDecimal(2.0).pow(w - 1 - fw) - BigDecimal(2.0).pow(-fw)
        }

        /**
         * @param fw Fractional part width
         * @return psilon (positive with min abs) FixPoint value in BigDecimal.
         */
        def epsilonInBigDecimal(fw: Int): BigDecimal = {
            BigDecimal(2.0).pow(-fw)
        }

        /**
         * @param w Total width
         * @param fw Fractional part width
         * @param value value to be tested
         * @return True if value can be fit into FixPoint
         */
        def inRange(w: Int, fw: Int, value: Double): Boolean = {
            lowestInDouble(w, fw) <= value && value <= highestInDouble(w, fw)
        }

        /**
         * @param w Total width
         * @param fw Fractional part width
         * @param value value to be tested
         * @return True if value can be fit into FixPoint after round to nearest quantitative steps.
         * @note The round mode is specified in FixPoint.roundMode.
         */
        def inRangeAfterRound(w: Int, fw: Int, value: Double): Boolean = {
            val rounded = FixPoint.round(value * math.pow(2.0, fw)) * math.pow(2.0, -fw)
            lowestInDouble(w, fw) <= rounded && rounded <= highestInDouble(w, fw)
        }

        /**
         * @param w Total width
         * @param fw Fractional part width
         * @param value value to be tested
         * @return True if value can be fit into FixPoint
         */
        def inRange(w: Int, fw: Int, value: BigDecimal): Boolean = {
            val low = lowestInBigDecimal(w, fw)
            val high = highestInBigDecimal(w, fw)
            low <= value && value <= high
        }

        /**
         * @param w Total width
         * @param fw Fractional part width
         * @param value value to be tested
         * @return True if value can be fit into FixPoint after round to nearest quantitative steps
         * @note The round mode is specified in FixPoint.roundMode.
         */
        def inRangeAfterRound(w: Int, fw: Int, value: BigDecimal): Boolean = {
            // val rounded = (value * BigDecimal(2.0).pow(fw)).setScale(0, BigDecimal.RoundingMode.HALF_UP) * BigDecimal(2.0).pow(-fw)
            val rounded = BigDecimal(FixPoint.round(value * BigDecimal(2.0).pow(fw))) * BigDecimal(2.0).pow(-fw)
            lowestInBigDecimal(w, fw) <= rounded && rounded <= highestInBigDecimal(w, fw)
        }
    }

    implicit class FromDoubleToFixPoint(private val x: Double) extends AnyVal {
        /**
         * Convert this double to FixPoint.
         * @param width total width
         * @param fw fractional part width
         * @return the FixPoint hardware represent the value of double.
         * @note
         *  - Rntime requirement violation will occur if value can not be fitted in specified widths.
         *  - Value will be quantified due to limited fractional part width (loss precision).
         *  - The round mode is specified in FixPoint.roundMode.
         */
        def toFixPoint(width: Width, fw: Int): FixPoint = {
            val res = Wire(FixPoint(width, fw))
            res := x
            res
        }

        /**
         * Convert this double to FixPoint.
         * @param width total width
         * @param fw fractional part width
         * @return the FixPoint hardware represent the value of double.
         * @note
         *  - Runtime requirement violation will occur if value can not be fitted in specified widths.
         *  - Value will be quantified due to limited fractional part width (loss precision).
         *  - The round mode is specified in FixPoint.roundMode.
         */
        def F(width: Width, fw: Int): FixPoint = {
            val res = Wire(FixPoint(width, fw))
            res := x
            res
        }

        /**
         * Get raw bit (as Long) of the FixPoint presenting this double.
         * @param width total width
         * @param fw fractional part width
         * @return Raw bit of the FixPoint represent the value of double.
         */
        def toFixPoint(width: Int, fw: Int): Long = {
            require(width <= 64 && fw < 64)
            require(FixPoint.inRange(width, fw, x))
            // (x * math.pow(2.0, fw)).round
            FixPoint.round(x * math.pow(2.0, fw))
        }
    }

    implicit class FromBigDecimalToFixPoint(private val x: BigDecimal) extends AnyVal {
        /**
         * Convert this BigDecimal to FixPoint.
         * @param width total width
         * @param fw fractional part width
         * @return the FixPoint hardware represent the value of BigDecimal.
         * @note
         *  - Runtime requirement violation will occur if value can not be fitted in specified widths.
         *  - Value will be quantified due to limited fractional part width (loss precision).
         *  - The round mode is specified in FixPoint.roundMode.
         */
        def toFixPoint(width: Width, fw: Int): FixPoint = {
            val res = Wire(FixPoint(width, fw))
            res := x
            res
        }

        /**
         * Convert this BigDecimal to FixPoint.
         * @param width total width
         * @param fw fractional part width
         * @return the FixPoint hardware represent the value of BigDecimal.
         * @note
         *  - Runtime requirement violation will occur if value can not be fitted in specified widths.
         *  - Value will be quantified due to limited fractional part width (loss precision).
         *  - The round mode is specified in FixPoint.roundMode.
         */
        def F(width: Width, fw: Int): FixPoint = {
            val res = Wire(FixPoint(width, fw))
            res := x
            res
        }

        /**
         * Get raw bit (as BigInt) of the FixPoint presenting this BigDecimal.
         * @param width total width
         * @param fw fractional part width
         * @return Raw bit of the FixPoint represent the value of BigDecimal.
         */
        def toFixPoint(width: Int, fw: Int): BigInt = {
            require(width <= 128 && fw < 128)
            require(FixPoint.inRange(width, fw, x))
            // (x * BigDecimal(2.0).pow(fw)).setScale(0, BigDecimal.RoundingMode.HALF_UP).toBigInt
            FixPoint.round(x * BigDecimal(2.0).pow(fw))
        }
    }

    implicit class DoubleUtils(private val x: Double) extends AnyVal {

        /**
         * Round half to +inf, alias of Double.round.
         * @return Round by half ceiling of this Double.
         */
        def halfCeil: Long = x.round

        /**
         * Round half to -inf
         * @return Round by half floor of this Double.
         */
        def halfFloor: Long = -(-x).round

        /**
         * Round half away from 0.
         * @return Round by half up of this Double.
         */
        def halfUp: Long = {
            if(x >= 0) x.round
            else -(-x).round
        }

        /**
         * Round half near to 0.
         * @return Round by half down of this Double.
         */
        def halfDown: Long = {
            if(x >= 0) -(-x).round
            else x.round
        }

        /**
         * Round half to even number.
         * @return Round by half even of this Double.
         */
        def halfEven: Long = {
            if(x.toLong % 2L == 0L)
                x.halfDown
            else
                x.halfUp
        }

        // template<typename T = float>
        //         constexpr T PhaseCycle (T x)
        // {
        //     return std::floor((x + (T)M_PI) / (2 * (T)M_PI));
        // }

        /**
         * @return domain index of cycling [-pi, pi) domains.
         */
        def phaseCycle: Double = {
            math.floor((x + math.Pi) / (2.0 * math.Pi))
        }

        // template<typename T = float>
        //         constexpr T PhaseWrap(T x)
        // {
        //     return x - 2 * (T)M_PI * PhaseCycle(x);
        // }

        /**
         * @return wrap phase into [-pi, pi) domains.
         */
        def phaseWrap: Double = {
            x - 2.0 * math.Pi * phaseCycle
        }

        // template<typename T = float>
        //         constexpr T PhaseUnwrap(T x, T a)
        // {
        //     return x + 2 * (T)M_PI * PhaseCycle(a - x);
        // }

        /**
         * @param a reference phase.
         * @return unwrap phase to near the reference phase.
         */
        def phaseUnwrap(a: Double = 0.0): Double = {
            x + 2.0 * math.Pi * (a - x).phaseCycle
        }

        /**
         * @param y another phase.
         * @return phase diff in [-pi, pi) of two phases.
         */
        def phaseDiff(y: Double): Double = {
            (x - y).phaseUnwrap()
        }

        // template<typename T = float>
        //         constexpr T Clamp(T x, T l, T h)
        // {
        //     return x > h ? h :
        //             x < l ? l : x;
        // }

        /**
         * @param l low boundary
         * @param h high boundary
         * @return clamp this double into [l, h]
         * @note
         *  - same as clip(l, h)
         */
        def clamp(l: Double, h: Double): Double = {
            if (x > h) h
            else if (x < l) l
            else x
        }

        /**
         * @param l low boundary
         * @param h high boundary
         * @return clip this double into [l, h]
         * @note
         *  - same as clamp(l, h)
         */
        def clip(l: Double, h: Double): Double = {
            if (x > h) h
            else if (x < l) l
            else x
        }

        /**
         * @param l negative corrosion amount
         * @param h positive corrosion amount
         * @return corroded this double by specified amount
         * @note
         *  - if x > h, return x - h
         *  - if x < l, return x - l
         */
        def corrod(l: Double, h: Double): Double = {
            if (x > h) x - h
            else if (x < l) x - l
            else 0.0
        }

        // template <typename T>
        // inline T Map2Unit(T x, T l, T h)
        // {
        // return (x - l) / (h - l);
        // }

        /**
         * @param l low boundary of range
         * @param h high boundary of range
         * @return map linearly this double in specified range into [0, 1]
         */
        def mapRange2Unit(l: Double, h: Double): Double = {
            (x - l) / (h - l)
        }

        // template <typename T>
        // inline T Map2Range(T x, T l, T h)
        // {
        // return x * (h - l) + l;
        // }

        /**
         * @param l low boundary of range
         * @param h high boundary of range
         * @return map linearly this double in [0, 1] into [l, h]
         */
        def mapUnit2Range(l: Double, h: Double): Double = {
            x * (h - l) + l
        }

        /**
         * @param fromL low boundary of source range
         * @param fromH high boundary of source range
         * @param toL low boundary of destination range
         * @param toH high boundary of destination range
         * @return map linearly this double in [fromL, formH] into [toL, toH]
         */
        def mapFromTo(fromL: Double, fromH: Double, toL: Double, toH: Double): Double = {
            (x - fromL) / (fromH - fromL) * (toH - toL) + toL
        }

        // template <typename T>
        // inline T Wrap(T x, T l, T h)
        // {
        // T d = h - l;
        // return x - std::floor((x - l) / d) * d;
        // }

        /**
         * @param l low boundary of domain
         * @param h high boundary of domain
         * @return domain index of cycling [l, h) domains.
         */
        def wrap(l: Double, h: Double): Double = {
            val d = h - l
            x - math.floor((x - l) / d) * d
        }

        // template <typename T>
        // inline T Unwrp(T x, T l, T h, T a)
        // {
        // T d = (h - l) * (T)0.5;
        // return x - a >  d ? x - (h - l) :
        //         x - a < -d ? x + (h - l) : x;
        // }

        /**
         * @param l low boundary of domain
         * @param h high boundary of domain
         * @param a reference value
         * @return unwrap this double to near the reference value.
         */
        def unwrap(l: Double, h: Double, a: Double = 0.0): Double = {
            val d = (h - l) * 0.5
            if (x - a > d)
                x - (h - l)
            else if (x - a < -d)
                x + (h - l)
            else
                x
        }
    }

    implicit class BigDecimalUtils(private val x: BigDecimal) extends AnyVal {
        def halfUp: BigInt = {
            x.setScale(0, BigDecimal.RoundingMode.HALF_UP).toBigInt
        }
        def halfDown: BigInt = {
            x.setScale(0, BigDecimal.RoundingMode.HALF_DOWN).toBigInt
        }
        def halfEven: BigInt = {
            x.setScale(0, BigDecimal.RoundingMode.HALF_EVEN).toBigInt
        }
        def halfCeil: BigInt = {
            if(x >= BigDecimal(0.0))
                x.setScale(0, BigDecimal.RoundingMode.HALF_UP).toBigInt
            else
                x.setScale(0, BigDecimal.RoundingMode.HALF_DOWN).toBigInt
        }
        def halfFloor: BigInt = {
            if(x >= BigDecimal(0.0))
                x.setScale(0, BigDecimal.RoundingMode.HALF_DOWN).toBigInt
            else
                x.setScale(0, BigDecimal.RoundingMode.HALF_UP).toBigInt
        }
    }

    /**
     * Greatest common divisor of 2 integer
     *
     * @param u One integer
     * @param v Another integer
     * @tparam T Type of integers
     * @return GCD of `u` and `v`
     */
    def GCD[T: Integral](u: T, v: T): T = {
        val int = implicitly[Integral[T]]
        import int.mkNumericOps
        if (v == 0) u
        else GCD(v, u % v)
    }

    /**
     * Least common multiple of 2 integer
     *
     * @param u One integer
     * @param v Another integer
     * @tparam T Type of integer
     * @return LCM of `u` and `v`
     */
    def LCM[T: Integral](u: T, v: T): T = {
        val int = implicitly[Integral[T]]
        import int.mkNumericOps
        u * v / GCD(u, v)
    }

    def phaseDiff(x: Double, y: Double): Double = {
        (x - y).phaseUnwrap()
    }

    def asinh(x: Double): Double = {
        // import scala.math._
        log(x + sqrt(x * x + 1))
    }

    def acosh(x: Double): Double = {
        // import scala.math._
        if (x < 1) Double.NaN
        else log(x + sqrt(x * x - 1))
    }

    def atanh(x: Double): Double = {
        // import scala.math._
        if (x.abs >= 1) Double.NaN
        else 0.5 * log((1 + x) / (1 - x))
    }

    def elemWiseDiff[T: Numeric](x: Seq[T], y: Seq[T]): Seq[T] = {
        val num = implicitly[Numeric[T]]
        import num.mkNumericOps
        x.zip(y).map { case (x, y) => x - y }
    }

    def maxAndRms[T: Numeric](x: Seq[T]): (T, Int, Double) = {
        val num = implicitly[Numeric[T]]
        import num._
        val (max, mi, pwr) = x.zipWithIndex.foldLeft((zero, -1, 0.0)) {
            case ((max, mi, pwr), (x, i)) => {
                val abs = x.abs
                val sqr = x.toDouble * x.toDouble
                val (new_max, new_mi) = if (abs > max) (abs, i) else (max, mi)
                (new_max, new_mi, pwr + sqr)
            }
        }
        (max, mi, math.sqrt(pwr / x.length))
    }

    def minAndIndices[T: Numeric](x: Seq[T]): (T, Seq[Int]) = {
        val min = x.min
        val minIndices = x.zipWithIndex.collect{
            case (v, i) if v == min => i
        }
        (min, minIndices)
    }

    def maxAndIndices[T: Numeric](x: Seq[T]): (T, Seq[Int]) = {
        val max = x.max
        val maxIndices = x.zipWithIndex.collect{
            case (v, i) if v == max => i
        }
        (max, maxIndices)
    }

    def ReadCsvData(fileName: String, encoding: String = "UTF-8"): Array[Array[Double]] = {
        import java.nio.file.{Files, Paths}
        import scala.io.Source
        import scala.collection.mutable.ArrayBuffer

        val exist: Boolean = Files.exists(Paths.get(fileName))
        if(exist) {
            val colBuffer = ArrayBuffer[Array[Double]]()
            val source = Source.fromFile(fileName, encoding)
            val lines = source.getLines()
            lines.foreach(l => {
                val elems = l.split(',')
                val row = elems.map(e => {
                    try { e.toDouble }
                    catch { case _: Throwable => Double.NaN }
                })
                colBuffer += row
            })
            source.close()
            colBuffer.toArray
        }
        else {
            Array.empty[Array[Double]]
        }
    }

}

final def <<<(n: UInt): FixPoint = shiftLeft(n)


import chisel3._
import loywong.util._

class abcd {
    val _a = Wire(Bool())
    val _b = Wire(Bool())
    val _c = _a ^ _b
    val _d = !(_a ^ _b)
    def con_ac(a: Bool, c: Bool) = {
        _a := a
        c := _c
    }
    def con_bd(b: Bool, d: Bool) = {
        _b := b
        d := _d
    }
}

class test_abcd extends Module {
    //val io = FlatIO(new Bundle {
        val a = IO( Input(Bool()))

        val c = IO(Output(Bool()))
        val d = IO(Output(Bool()))
    //})
    val the_abcd = new abcd
    the_abcd.con_ac(a, c)
    the_abcd.con_bd(b, d)
}

class test_catbytewise extends Module {
    val io = IO(new Bundle {
        val a = Input(UInt(3.W))
        val b = Input(SInt(9.W))
        val c = Input(UInt(8.W))
        val d = Input(SInt(12.W))
        val o = Output(UInt(48.W))
    })
    io.o := CatByteWise(io.d, io.c, io.b, io.a)
}

class test_slices extends Module {
    val io = IO(new Bundle {
        val x = Input(UInt(48.W))
        val a0 = Output(UInt(3.W))
        val b0 = Output(SInt(9.W))
        val c0 = Output(UInt(8.W))
        val d0 = Output(SInt(12.W))
        val a1 = Output(UInt(3.W))
        val b1 = Output(SInt(9.W))
        val c1 = Output(UInt(8.W))
        val d1 = Output(SInt(12.W))
        val a2 = Output(UInt(3.W))
        val b2 = Output(SInt(9.W))
        val c2 = Output(UInt(8.W))
        val d2 = Output(SInt(12.W))
        val a3 = Output(UInt(3.W))
        val b3 = Output(SInt(9.W))
        val c3 = Output(UInt(8.W))
        val d3 = Output(SInt(12.W))
    })
    io.x.sliceTo(io.d0, io.c0, io.b0, io.a0)
    io.x.sliceByteWiseTo(io.d1, io.c1, io.b1, io.a1)

    val sbits = io.x.slices(12, 8, 9, 3)
    io.d2 := sbits(0).asSInt
    io.c2 := sbits(1)
    io.b2 := sbits(2).asSInt
    io.a2 := sbits(3)

    val sbytes = io.x.slicesByteWise(12, 8, 9, 3)
    io.d3 := sbytes(0).asSInt
    io.c3 := sbytes(1)
    io.b3 := sbytes(2).asSInt
    io.a3 := sbytes(3)
}

class test_fp extends Module {
    val io = IO(new Bundle {
        val a = Input(FixPoint(10.W, 8))
        val b = Input(FixPoint(16.W, 12))
        val c = Input(SInt(10.W))
        val i = Input(FixPoint(12.W, 11))
        val q = Input(FixPoint(8.W, 7))
        val add0 = Output(FixPoint(16.W, 12))
        val sub0 = Output(FixPoint(16.W, 12))
        val prod0 = Output(FixPoint(28.W, 16))
        val add1 = Output(FixPoint(12.W, 8))
        val sub1 = Output(FixPoint(12.W, 8))
        val prod1 = Output(FixPoint(15.W, 8))
        val add2 = Output(FixPoint(12.W, 8))
        val sub2 = Output(FixPoint(12.W, 8))
        val prod2 = Output(FixPoint(15.W, 8))
        val ca = Output(FixPoint(16.W, 12))
        val cb = Output(FixPoint(12.W, 8))
        val cc = Output(FixPoint(16.W, 6))
        val c0 = Output(FixPoint(16.W, 15))
        val c1 = Output(FixPoint(16.W, 8))
        val c2 = Output(FixPoint(16.W, 8))
        val c1zero = Output(FixPoint(16.W, 8))
        val c1negative = Output(FixPoint(17.W, 8))
        val c1int = Output(SInt(8.W))
        val c1frac = Output(FixPoint(9.W, 8))
        val c1floor = Output(FixPoint(8.W, 0))
        val c1ceil = Output(FixPoint(9.W, 0))
        val mag = Output(FixPoint(12.W, 11))
        val ff00 = Output(FixPoint(16.W, 3))
        val ff01 = Output(FixPoint(16.W, 3))
        val ff02 = Output(FixPoint(16.W, 3))
        val ff03 = Output(FixPoint(16.W, 3))
        val ff04 = Output(FixPoint(16.W, 3))
        val ff05 = Output(FixPoint(16.W, 3))
        val ff06 = Output(FixPoint(16.W, 3))
        val ff07 = Output(FixPoint(16.W, 3))
        val ff08 = Output(FixPoint(16.W, 3))
    })

    // println(s"Q2.8 min: ${io.a.lowest}, max: ${io.a.highest}")

    io.add0 := io.a + io.b
    io.sub0 := io.a - io.b
    io.prod0 := io.a * io.b
    io.add1 := (io.a + io.b).truncate(8)
    io.sub1 := (io.a - io.b).truncate(8)
    io.prod1 := (io.a * io.b).truncate(8)
    io.add2 := (io.a + io.b).round(8)
    io.sub2 := (io.a - io.b).round(8)
    io.prod2 := (io.a * io.b).round(8)
    io.ca := io.a
    io.cb := io.b
    io.cc := io.c
    io.c0 := 0.75
    io.c1 := -12.3
    io.c2 := 12.3.F(16.W, 8)
    // io.c1 := 128.0  // error
    io.c1zero := io.c1.zero
    io.c1negative := io.c1.negative
    io.c1int := io.c1.toSInt
    io.c1frac := io.c1.getPureFraction
    io.c1floor := io.c1.floor()
    io.c1ceil := io.c1.ceil()

    // val zzz = RegInit(new FixPoint(SInt(8.W), 7))

    val xxx = RegInit(FixPoint(0.S(8.W), 7))
    val sqi = RegInit(FixPoint(12.W, 11, 0.5))
    val sqq = RegInit(-0.5.F(8.W, 7))

    val mag = Reg(FixPoint(12.W, 11))

    val fp00 = Wire(FixPoint.byLowest(3, -3.3))
    fp00 := -3.3
    val fp01 = Wire(FixPoint.byHighest(3, 3.3))
    fp01 := 3.3
    val fp02 = Wire(FixPoint.byLowest(3, -2.0))
    fp02 := -2.0
    val fp03 = Wire(FixPoint.byHighest(3, 1.875))
    fp03 := 1.875
    val fp04 = Wire(FixPoint.byLowest(3, -2.0624))
    fp04 := -2.0624
    val fp05 = Wire(FixPoint.byLowest(3, -2.0625))
    fp05 := -2.0625
    val fp06 = Wire(FixPoint.byHighest(3, 1.9374))
    fp06 := 1.9374
    val fp07 = Wire(FixPoint.byHighest(3, 1.9375))
    fp07 := 1.9375
    val fp08 = Wire(FixPoint.byRange(3, -2.0624, 1.9374))
    fp08 := 0.1

    val halfs = (-4 until 4).map(_.toDouble + 0.5)
    val halfUps = halfs.map(_.halfUp)
    val halfDowns = halfs.map(_.halfDown)
    val halfEvens = halfs.map(_.halfEven)
    val halfCeils = halfs.map(_.halfCeil)
    val halfFloors = halfs.map(_.halfFloor)

    val bdHalfs = (-4 until 4).map(BigDecimal(_) + 0.5)
    val bdHalfCeils = bdHalfs.map(_.halfCeil)
    val bdHalfFloors = bdHalfs.map(_.halfFloor)

    io.ff00 := fp00
    io.ff01 := fp01
    io.ff02 := fp02
    io.ff03 := fp03
    io.ff04 := fp04
    io.ff05 := fp05
    io.ff06 := fp06
    io.ff07 := fp07
    io.ff08 := fp08

    sqi := (io.i * io.i).truncate(11)
    sqq := (io.q * io.q).truncate(7)
    mag := sqi + sqq
    io.mag := mag
}

val b = IO( Input(Bool())).suggestName{"bbb"}


/*
 * MIT License
 *
 * Copyright (c) 2025 loykylewong (loywong@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package loywongtest

import chisel3._
import chisel3.util._
import chiseltest._
import chiseltest.internal.TesterThreadList

import loywong._
import loywong.util._

/**
 * An AXI4-Lite Master for test-bench.
 * @param clk   The clock associated with the AXI4-Lite IO
 * @param axi4l The AXI4-Lite Slave IO to be tested
 */
class Axi4LiteTestMaster(clk: Clock, axi4l: Axi4LiteIO) {

    // private val addrWidth = axi4l.ar.bits.addr.getWidth
    private val dataBytes = axi4l.r.bits.data.getWidth / 8
    private val dataMask = (BigInt(1) << (dataBytes * 8)) - 1
    private val strbMask = (BigInt(1) << dataBytes) - 1

    /**
     * Issue a write transaction
     *
     * @param addr Address of the write transaction
     * @param data Data to be written
     * @param strb Strb associated to the data
     * @return Slave response
     */
    def write(addr: BigInt, data: BigInt, strb: BigInt = strbMask): Int = {
        axi4l.aw.bits.addr.poke(addr)
        axi4l.aw.bits.prot.poke(0)
        axi4l.aw.valid.poke(true)
        axi4l.w.bits.data.poke(data & dataMask)
        axi4l.w.bits.strb.poke(strb & strbMask)
        axi4l.w.valid.poke(true)

            clk.step() // aw handshake cycle
            axi4l.aw.valid.poke(false)
        }
        val wt = fork {
            while (!axi4l.w.ready.peekBoolean()) {
                clk.step()
            }
            clk.step() // w handshake cycle
            axi4l.w.valid.poke(false)
        }
        awt.join()
        wt.join()
        while (!axi4l.b.valid.peekBoolean()) {
            clk.step()
        }
        val resp = axi4l.b.bits.resp.peekInt()
        clk.step() // b handshake cycle
        resp.toInt
    }

    /**
     * Issue a read transaction
     *
     * @param addr Address of the read transaction
     * @return (data, resp)
     *         - data: Data read
     *         - resp: Slave response
     */
    def read(addr: BigInt): (BigInt, Int) = {
        axi4l.ar.bits.addr.poke(addr)
        axi4l.ar.bits.prot.poke(0)
        axi4l.ar.valid.poke(true)
        axi4l.r.ready.poke(true)
        while (!axi4l.ar.ready.peekBoolean()) {
            clk.step()
        }
        clk.step() // ar handshake cycle
        axi4l.ar.valid.poke(false)
        while (!axi4l.r.valid.peekBoolean()) {
            clk.step()
        }
        val data = axi4l.r.bits.data.peekInt()
        val resp = axi4l.r.bits.resp.peekInt().toInt
        clk.step() // r handshake cycle
        (data, resp)
    }
}

        axi4l.b.ready.poke(true)
        val awt = fork {
            while (!axi4l.aw.ready.peekBoolean()) {