package examples.leros

import chisel3._
import chisel3.experimental.BundleLiterals.AddBundleLiteralConstructor
import chiseltest._
import chiseltest.ChiselScalatestTester
import org.scalatest.BeforeAndAfterAll
import org.scalatest.flatspec.AnyFlatSpec
import scala.math.pow

import chiselverify.coverage.{cover => ccover, _}
import chiselverify.coverage.CoverReport._
import chiselverify.crv.{RangeBinder, ValueBinder}
import chiselverify.crv.backends.jacop._

case class AluAccumulator(value: BigInt)

class AluTransaction(seed: Int, size: Int) extends RandObj {
  currentModel = new Model(seed)
  val max = pow(2, size).toInt

  val op: RandVar = rand(0, 7)
  val din: RandVar = rand(0, max)
  val ena: RandVar = rand(0, 1)

  val values: DistConstraint = din dist (
    0 to 0xF := 1,
    0xF to 0xFF := 1,
    0xFF to 0xFFF := 1,
    0xFFF to 0xFFFF := 1,
  )

  val onlyHigh: DistConstraint = din dist (
    0xFF to 0xFFF := 1,
    0xFFF to 0xFFFF := 10,
  )

  val enaHigh: DistConstraint = ena dist (
    1 := 99,
    0 := 1
  )

  onlyHigh.disable()

  def toBundle: AluAccuInput = {
    new AluAccuInput(size).Lit(_.op -> op.value().U, _.din -> din.value().U, _.ena -> ena.value().B)
  }
}

object AluGoldenModel {

  def genNextState(transaction: AluAccuInput, accu: AluAccumulator): AluAccumulator = {
    val mask: Int = (1 << transaction.din.getWidth) - 1
    if (transaction.ena.litToBoolean) {
      transaction.op.litValue.toInt match {
        case 0 => accu
        case 1 => if ((accu.value + transaction.din.litValue) > mask) {
            AluAccumulator((accu.value + transaction.din.litValue) & mask)
        } else {
            AluAccumulator(accu.value + transaction.din.litValue)
        }
        case 2 => if ((accu.value - transaction.din.litValue) < 0) {
            AluAccumulator((accu.value - transaction.din.litValue) & mask)
        } else {
            AluAccumulator(accu.value - transaction.din.litValue)
        }
        case 3 => AluAccumulator(accu.value & transaction.din.litValue)
        case 4 => AluAccumulator(accu.value | transaction.din.litValue)
        case 5 => AluAccumulator(accu.value ^ transaction.din.litValue)
        case 6 => AluAccumulator(transaction.din.litValue)
        case 7 => AluAccumulator(accu.value.toInt >>> 1)
      }
    } else {
      accu
    }
  }

  def genOutputStates(listT: List[AluAccuInput], accu: AluAccumulator): List[AluAccumulator] = {
    listT match {
      case Nil => Nil
      case x :: xs =>
        val nexAccu = genNextState(x, accu)
        nexAccu :: genOutputStates(xs, nexAccu)
    }
  }

  def serializeAccu(accu: AluAccumulator, size: Int): AluAccuOutput =
    new AluAccuOutput(size).Lit(_.accu -> accu.value.U)

  def generateAluAccuTransactions(listT: List[AluAccuInput], accu: AluAccumulator): List[AluAccuOutput] =
    genOutputStates(listT, accu).map(serializeAccu(_, listT.head.din.getWidth))

  def compareSingle(transaction: (AluAccuOutput, AluAccuOutput)): Boolean = {
    val (dutT, genT) = transaction
    equals(dutT.accu.litValue == genT.accu.litValue)
    dutT.accu.litValue == genT.accu.litValue
  }

  def compare(transactions: List[(AluAccuOutput, AluAccuOutput)]): List[Boolean] = transactions.map(compareSingle(_))
}

trait AluBehavior {
  this: AnyFlatSpec with ChiselScalatestTester =>
  val coverageCollector = new CoverageCollector

  def compute(name: String, size: Int, inputT: List[AluAccuInput]): Unit = {
    it should s"test alu with $name" in {
      test(new AluAccuMultiChisel(size)) { dut =>
        val cr = new CoverageReporter(dut)
        cr.register(
          ccover("op", dut.input.op)(
            bin("nop", 0 to 0),
            bin("add", 1 to 1),
            bin("sub", 2 to 2),
            bin("and", 3 to 3),
            bin("or", 4 to 4),
            bin("xor", 5 to 5),
            bin("ld", 6 to 6),
            bin("shr", 7 to 7)),
          ccover("din", dut.input.din)(
            bin("0xF", 0 to 0xF),
            bin("0xFF", 0xF to 0xFF),
            bin("0xFFF", 0xFF to 0xFFF),
            bin("0xFFFF", 0xFFF to 0xFFFF),
          ),
          ccover("accu", dut.output.accu)(
            bin("0xF", 0 to 0xF),
            bin("0xFF", 0xF to 0xFF),
            bin("0xFFF", 0xFF to 0xFFF),
            bin("0xFFFF", 0xFFF to 0xFFFF),
          ),
          ccover("ena", dut.input.ena)(
            bin("disabled", 0 to 0),
            bin("enabled", 1 to 1)
          ),
          ccover("operations cross enable", dut.input.op, dut.input.ena)(
            cross("operation enable", Seq(0 to 7, 1 to 1)),
            cross("operation disabled", Seq(0 to 7, 0 to 0))
          )
        )

        val computedTransactions: List[AluAccuOutput] = inputT.map { trx =>
          dut.input.poke(trx)
          dut.clock.step()
          cr.sample()
          dut.output.peek()
        }

        val goldenTransactions = AluGoldenModel.generateAluAccuTransactions(inputT, AluAccumulator(0))
        AluGoldenModel.compare(computedTransactions zip goldenTransactions)
        coverageCollector.collect(cr.report)
      }
    }
  }
}

class AluVerification extends AnyFlatSpec with AluBehavior with ChiselScalatestTester with BeforeAndAfterAll {
  behavior of "AluAccumulator"
  val size = 16
  val IsUnitTest = true // set to false to generate a lot more transactions

  val seeds = if(IsUnitTest) { List(30, 104) } else {
    List(30, 104, 60, 90, 200, 50, 22, 2000, 40, 900, 70, 23)
  }

  println(s"Testing ALU with ${tnumber * seeds.size * 2} random transactions")
  // General test
  seeds.foreach { seed =>
    val length = tnumber
    val masterT = new AluTransaction(seed, size)
    val transactions: Seq[AluAccuInput] = (0 to length) map { _ =>
      masterT.randomize
      masterT.toBundle
    }
    it should behave like compute(s"seed = $seed, and normal values", size, transactions.toList)
  }

  // Only transactions with values between 0xFF to 0xFFFF
  seeds.foreach { seed =>
    val length = tnumber
    val masterT = new AluTransaction(seed, size)
    val onlyHighTransactions: Seq[AluAccuInput] = (0 to length) map { _ =>
      masterT.onlyHigh.enable()
      masterT.values.disable()
      masterT.randomize
      masterT.toBundle
    }
    it should behave like compute(s"seed = $seed, and high values", size, onlyHighTransactions.toList)
  }
}

val tnumber = if(IsUnitTest) { 10 } else { 5000 }


package ofdm

import chisel3._
import chisel3.iotesters.PeekPokeTester
import freechips.rocketchip.amba.axi4stream._
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import org.scalatest.{FlatSpec, Matchers}

class AXI4StreamTimeAdapaterTestModule extends Module {

  implicit val p = Parameters.empty
  val lm = LazyModule(new LazyModule {
    val fuzzerADC  = AXI4StreamFuzzer(AXI4StreamTransaction.linearSeq(100))
    val fuzzerDAC  = AXI4StreamFuzzer(Seq.tabulate(100)(x => AXI4StreamTransaction(data = 100 + x, user = 100 + x)), u = 64)
    val (adc, dac) = AXI4StreamTimeAdapter()
    val adcOut     = AXI4StreamSlaveNode(AXI4StreamSlaveParameters())
    val dacOut     = AXI4StreamSlaveNode(AXI4StreamSlaveParameters())

    adcOut := adc := fuzzerADC
    dacOut := dac := fuzzerDAC

    lazy val module = new LazyModuleImp(this) {
      val (a, aOutEdge) = adcOut.in.head
      val (d, dOutEdge) = dacOut.in.head
      val aOut = IO(AXI4StreamBundle(aOutEdge.bundle))
      val dOut = IO(AXI4StreamBundle(dOutEdge.bundle))
      aOut <> a
      dOut <> d
    }
  })
  val m = Module(lm.module)

  val io = IO(new Bundle {
    val adc = AXI4StreamBundle(m.aOutEdge.bundle)
    val dac = AXI4StreamBundle(m.dOutEdge.bundle)
  })

  io.adc <> m.aOut
  io.dac <> m.dOut
}

class AXI4StreamTimeAdapterTester(c: AXI4StreamTimeAdapaterTestModule)
  extends PeekPokeTester(c)
  with AXI4StreamSlaveModel {

  bindSlave(c.io.adc).addExpects(Seq.tabulate(100)(d => AXI4StreamTransactionExpect(data = Some(d))))
  bindSlave(c.io.dac).addExpects(Seq.tabulate(100)(x => AXI4StreamTransactionExpect(data = Some(100 + x))))

  stepToCompletion()
}

class AXI4StreamTimeAppenderSpec extends FlatSpec with Matchers {
  behavior of "AXI4StreamTimeAppender"

  it should "work with no DAC" in {

  }

  it should "work with multiple inputs to the DAC" ignore {

  }

  it should "work with multiple ADC inputs/outputs" ignore {

  }
}

chisel3.iotesters.Driver(() => new AXI4StreamTimeAdapaterTestModule) { new AXI4StreamTimeAdapterTester(_) } should be (true)


package ofdm

import freechips.rocketchip.amba.axi4.AXI4MasterModel
import breeze.math.Complex
import chisel3._
import chisel3.experimental._
import chisel3.iotesters.PeekPokeTester
import dsptools.numbers._
import freechips.rocketchip.amba.axi4._
import freechips.rocketchip.amba.axi4stream.{AXI4StreamFuzzer, AXI4StreamTransaction}
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImp, ValName}
import freechips.rocketchip.interrupts.{IntSinkNode, IntSinkPortSimple}
import ieee80211.IEEE80211
import org.scalatest.{FlatSpec, Matchers}

class AXI4SyncBlockTester[
// T <: Data,
M <: LazyModuleImp
//MultiIOModule with SyncBlockImp[
//  T, AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4EdgeParameters, AXI4EdgeParameters, AXI4Bundle
//  ]
](c: M)
  extends PeekPokeTester(c) with AXI4MasterModel {
  val outer = c.wrapper.asInstanceOf[SyncBlockTestModule[_]]
  override val memAXI: AXI4Bundle = outer.module.in
  val intbundles = outer.module.intout

  axiWriteWord(0,  64)
  axiWriteWord(8,  64)
  axiWriteWord(24, 160)
  axiWriteWord(32, 100)

  step(1000)
}

class AXI4MasterPort(params: AXI4MasterPortParameters)(implicit p: Parameters) extends LazyModule {
  val node = AXI4MasterNode(Seq(params))

  lazy val module = new LazyModuleImp(this) {
    val (ins,  inEdgeParams)  = node.in.unzip
    val (outs, outEdgeParams) = node.out.unzip

    ins.zip(outs).foreach { case (i, o) => o <> i }
  }
}

object AXI4MasterPort {
  def apply(params: AXI4MasterPortParameters =
            AXI4MasterPortParameters(Seq(AXI4MasterParameters("axi4master"))))
           (implicit p: Parameters, valName: ValName): AXI4MasterNode = {
    LazyModule(new AXI4MasterPort(params)).node
  }
}

class SyncBlockTestModule[T <: Data : Real]
(
  proto: T,
  maxNumPeaks: Int = 32,
  autocorrParams: AutocorrParams[DspComplex[T]],
  transactions: Seq[AXI4StreamTransaction] = AXI4StreamTransaction.linearSeq(100)
)(implicit p: Parameters) extends LazyModule {
  val sync = LazyModule(new AXI4SyncBlock(proto, maxNumPeaks = maxNumPeaks, autocorrParams = autocorrParams))
  val memMaster = AXI4MasterNode(Seq(AXI4MasterPortParameters(Seq(AXI4MasterParameters("axi4master")))))
  sync.mem.get := memMaster
  val fuzzer = AXI4StreamFuzzer(transactions = transactions, u = 64)
  sync.streamNode := fuzzer
  val intnode = IntSinkNode(IntSinkPortSimple(ports = 1, sinks = 1))
  intnode := sync.intnode

  lazy val module = new LazyModuleImp(this) {
    val in = IO(Flipped(AXI4Bundle(memMaster.out.head._1.params)))
    memMaster.out.head._1 <> in

    val intout = IO(Output(Vec(intnode.in.length, Bool())))
    intout := intnode.in.head._1

    // val ints = IO(Vec(sync.intnode.in.length, Bool()))
    // ints.zip(sync.intnode.in.map(_._1)).map(_ <> _)
  }
}

trait TestSyncBlock[T <: Data] extends AXI4SyncBlock[T] {
  def trans: Seq[AXI4StreamTransaction]
}

class SyncBlockSpec extends FlatSpec with Matchers {
  behavior of "Sync Block"

  implicit val p = Parameters.empty
  def axiDut[T <: Data : Real]
  (
    proto: T,
    maxNumPeaks: Int = 512,
    autocorrParams: AutocorrParams[DspComplex[T]],
    transactions: Seq[AXI4StreamTransaction] = AXI4StreamTransaction.linearSeq(100)
  )(implicit valName: ValName) = {
    LazyModule(new SyncBlockTestModule(
      proto, maxNumPeaks = maxNumPeaks, autocorrParams = autocorrParams, transactions = transactions
    )).module
  }

  it should "detect packets" in {
    val proto = FixedPoint(32.W, 14.BP)

    def complexToBigInt(c: Complex): BigInt = {
      val real = FixedPoint.toBigInt(c.real, binaryPoint = proto.binaryPoint.get - 2)
      val imag = FixedPoint.toBigInt(c.imag, binaryPoint = proto.binaryPoint.get - 2)
      (unsignedBigInt(real) << proto.getWidth) + unsignedBigInt(imag)
    }

    chisel3.iotesters.Driver.execute( Array("-fiwv", "-tbn", "verilator"),
      () => axiDut
        (
          proto,
          autocorrParams = AutocorrParams(DspComplex(proto),
            512,
            512),
          transactions = (IEEE80211.stf ++ Seq.fill(500)(Complex(0, 0)) ++ IEEE80211.stf ++ Seq.fill(500)(Complex(0, 0))).zipWithIndex.map { case (c, i) =>
              AXI4StreamTransaction(data = complexToBigInt(c), user = i)
          }
        )
    ) {
      c => new AXI4SyncBlockTester(c)
    } should be (true)
  }

}

    def unsignedBigInt(i: BigInt): BigInt = if (i < 0) {
      (BigInt(2) << proto.getWidth) + i
    } else {


package ldpc

import chisel3._
import chisel3.util.Decoupled
import ofdm.TreeReduce

object SWEncoder {
  def apply(word: Seq[Boolean], params: LdpcParams): Seq[Boolean] = {
    require(word.length == params.generator.length)
    val selectedRows: Seq[Seq[Boolean]] = word.zip(params.generator).map({ case (w: Boolean, g: Seq[Boolean]) =>

    selectedRows.foldLeft[Seq[Boolean]](Seq.fill(selectedRows.head.length)(false)) { case (prod, row) =>
        prod.zip(row).map({ case (l, r) => l ^ r})
    }
  }
}

class Encoder(params: LdpcParams) extends MultiIOModule {
  val in = IO(Flipped(Decoupled(Vec(params.k, Bool()))))
  val out = IO(Decoupled(Vec(params.n, Bool())))

  val selectedRows = in.bits.zip(params.generator.toSeq).map({ case (i: Bool, p: Seq[Boolean]) =>
    p.map(pp => if (pp) Some(i) else None)
  })

  def rowSum(top: Seq[Option[Bool]], bottom: Seq[Option[Bool]]): Seq[Option[Bool]] = {
    top.zip(bottom).map({
      case (Some(t), Some(b)) => Some(t ^ b)
      case (Some(t), None) => Some(t)
      case (None, Some(b)) => Some(b)
      case _ => None
    })
  }

  out.bits := VecInit(TreeReduce(selectedRows, rowSum).flatten)

  out.valid := in.valid
  in.ready := out.ready
}

if (w) g else Seq.fill(g.length)(false) })


package ofdm

import chisel3._
import chisel3.internal.requireIsHardware

object AdjustableShiftRegister {
  def apply[T <: Data](in: T, maxDepth: Int, depth: UInt, resetData: T, en: Bool = true.B): T = {
    require(maxDepth > 0)
    requireIsHardware(in)
    assert(depth <= maxDepth.U)

    val adjustableShiftRegister = Module(new chisel3.util.Queue(chiselTypeOf(in), entries = maxDepth, pipe = true, flow = true))

    adjustableShiftRegister.io.enq.bits := in
    adjustableShiftRegister.io.enq.valid := adjustableShiftRegister.io.count < depth && en

    adjustableShiftRegister.io.deq.ready := adjustableShiftRegister.io.count >= depth && en

    Mux(adjustableShiftRegister.io.deq.fire(), adjustableShiftRegister.io.deq.bits, resetData)


package ofdm

import chisel3._
import chisel3.internal.requireIsChiselType
import chisel3.util.{Decoupled, log2Ceil}

class CPRemover[T <: Data](proto: T, cpSize: Int, nFFT: Int) extends MultiIOModule {
  requireIsChiselType(proto)

  val in = IO(Flipped(Decoupled(proto)))
  val out = IO(Decoupled(proto))
  val tlastIn = IO(Input(Bool()))
  val tlastOut = IO(Output(Bool()))

  val maxCnt = cpSize + nFFT

  val cnt = RegInit(0.U(log2Ceil(maxCnt + 1).W))

  when (in.fire()) {
    when (cnt === (maxCnt - 1).U || tlastIn) {
      cnt := 0.U
    } .otherwise {

  out.valid := in.valid && cnt >= cpSize.U // only pass input to output when counter is past the cyclic prefix
  in.ready := out.ready || cnt < cpSize.U // accept input if output is ready, or if it is the cyclic prefix (ok to discard)

  tlastOut := tlastIn || (cnt === (maxCnt - 1).U)
}


package ofdm

import chisel3._
import chisel3.experimental.chiselName
import chisel3.util.Decoupled
import dsptools.numbers._

class QPSKDemodUnit[T <: Data : Real](protoIn: DspComplex[T], protoOut: T) extends MultiIOModule {
  val in = IO(Input(protoIn))
  val out = IO(Output(Vec(2, protoOut)))

  out(1) := in.real
}

class QPSKDemod[T <: Data : Real](params: RXParams[T]) extends MultiIOModule {
  val in = IO(Flipped(Decoupled(Vec(params.nDataSubcarriers, params.protoChannelEst))))
  val out = IO(Decoupled(Vec(params.nDataSubcarriers, Vec(2, params.protoLLR))))

  in.bits.zip(out.bits).foreach { case (i, o) =>
      val demod = Module(new QPSKDemodUnit(params.protoChannelEst, params.protoLLR))
      demod.in := i
      o := demod.out
  }
  out.valid := in.valid
  in.ready := out.ready
}


package ofdm

import chisel3._
import chisel3.util._
import breeze.math.Complex
import chisel3.experimental.{ChiselEnum, chiselName}
import chisel3.internal.requireIsChiselType
import dsptools.{DspContext, DspTester}
import dsptools.numbers._

object BitRev {
  def apply(x: BigInt, nBits: Int): BigInt = {
    val trailingBits = x.toString(2)
    assert(trailingBits.length <= nBits)
    val allBits = "0" * (nBits - trailingBits.length) + trailingBits
    BigInt(allBits.reverse, 2)
  }
}

class FixedButterflyIO[T <: Data : Ring](genIn: T, genOut: T, n: Int) extends Bundle {
  val in       = Input (Vec(n, DspComplex(genIn, genIn)))
  val out      = Output(Vec(n, DspComplex(genOut, genOut)))
}

class FixedButterfly[T <: Data : Ring : ConvertableTo](genIn: T, genOut: T, genTwiddle: T, twiddles: Map[Int, Seq[Complex]], n: Int) extends Module {
  val io = IO(new FixedButterflyIO(genIn, genOut, n))

  for (outIdx <- 0 until n) {
    require(twiddles.contains(outIdx), s"Twiddles don't contain $outIdx")
    val out = io.out(outIdx)
    val t = twiddles(outIdx)
    require(t.length == n, s"Twiddles should be length $n, got ${t.length}")

    val terms = io.in.zip(t) map { case (input, twiddle) =>
      twiddle match {
        case Complex(0, 0) => None
        case Complex(1, 0) => Some(input)
        case Complex(-1, 0) => Some(-input)
        case Complex(0, 1) => Some(input.mulj())
        case Complex(0, -1) => Some(-input.mulj())
        case Complex(r, i) =>
          val real = ConvertableTo[T].fromDouble(r, genTwiddle)
          val imag = ConvertableTo[T].fromDouble(i, genTwiddle)
          Some(input * DspComplex(real, imag))
      }
    } collect { case Some(x) => x }

    require(terms.nonEmpty, s"Output $outIdx has no twiddles!")

    out := TreeReduce(terms, (x: DspComplex[T], y: DspComplex[T]) => x + y)
  }
}

class Butterfly2[T <: Data : Ring](genIn: T, genOut: Option[T] = None) extends MultiIOModule {
  val in0  = IO(Input (genIn.cloneType))
  val in1  = IO(Input (genIn.cloneType))

  val sum  = in0 + in1
  val diff = in0 - in1

  val out0 = IO(Output(genOut.getOrElse(sum).cloneType))
  val out1 = IO(Output(genOut.getOrElse(diff).cloneType))

  out0 := sum
  out1 := diff
}

class StreamingFFTIO[T <: Data : Ring](genIn: T, genOut: T) extends Bundle {
  val in   = Input (Valid(DspComplex(genIn, genIn)))
  val last = Input(Bool())
  val out  = Output(Valid(DspComplex(genOut, genOut)))
}

trait HasStreamingFFTIO[T <: Data] {
  def io: StreamingFFTIO[T]
}

object SimulateStreamingFFT {
  def apply[T <: Data, M <: Module with HasStreamingFFTIO[T]]
  (
    dut: () => M,
    in: Seq[Complex],
    expects: Option[Seq[Complex]] = None,
    tolerance: Int = 14
  ): Seq[Complex] = {
    var out: Seq[Complex] = Seq()
    var expectsMutable = expects.getOrElse(Seq())
    dsptools.Driver.execute( dut, Array[String]("-tbn", "verilator") ) { c => new DspTester(c) {
      def checkOutput(): Unit = {
        fixTolLSBs.withValue(tolerance) {
          if (peek(c.io.out.valid)) {
            out = out :+ peek(c.io.out.bits)
            if (expectsMutable.nonEmpty) {
              expect(c.io.out.bits, expectsMutable.head)
              expectsMutable = expectsMutable.tail
            }
          }
        }
      }
      poke(c.io.in.valid, 1)
      for (i <- in) {
        poke(c.io.in.bits, i)
        step(1)
        checkOutput()
      }
      for (_ <- in.indices) {
        step(1)
        checkOutput()
      }
    }}
    out
  }
}

class R2SDF[T <: Data : Ring : ConvertableTo](val n: Int, val genIn: T, val genOut: T, val genTwiddle: Option[T] = None)
  extends Module with HasStreamingFFTIO[T] {

  require(isPow2(n), s"Radix 2 FFT must be power of 2")

  val io = IO(new StreamingFFTIO(genIn, genOut))

  val nStages = log2Ceil(n)

  val en = io.in.valid

  val genTwiddleResolved = genTwiddle.getOrElse(genIn)

  io.out.bits := delays.foldLeft(io.in.bits) { case (in, delay) =>
    val bf = Module(new Butterfly2(in))

    val shifted = if (delay <= 1 || true) {
      ShiftRegister(bf.out0, delay, en = en)
    } else {
      val shr = Module(new ShiftRegisterMem(chiselTypeOf(in), delay))
      shr.io.in.valid := en
      shr.io.in.bits  := bf.out0
      shr.io.depth.valid := reset
      shr.io.depth.bits := delay.U
      shr.io.out.bits
    }

    bf.in0 := shifted
    if (delay == 1) {
      bf.in1 := in.mulj()
    } else {
      bf.in1 := in
    }

    val twiddlesComplex = Seq.tabulate(delay) { i => RootsOfUnity(i * n / delay, n) }
    val twiddlesDspComplex = twiddlesComplex.map( c => DspComplex.wire(
      ConvertableTo[T].fromDoubleWithFixedWidth(c.real, genTwiddleResolved),
      ConvertableTo[T].fromDoubleWithFixedWidth(c.imag, genTwiddleResolved)
    ))

    val twiddles = VecInit(twiddlesDspComplex)
    val (twiddleCount, _) = Counter(en, delay * 2)

    val product =  bf.out1 * twiddles(twiddleCount)
    val out = Wire(chiselTypeOf(product))

    when (twiddleCount < delay.U) {
      out := DspComplex.wire(Ring[T].zero, Ring[T].zero)
    } .otherwise {
      out := product
    }

    out
  }

  io.out.valid := ShiftRegister(io.in.valid, n, false.B, true.B)
}

class BF2I[T <: Data : Ring](proto: T, val delay: Int) extends MultiIOModule {
  requireIsChiselType(proto)
  require(delay > 0)

  val en = IO(Input(Bool()))
  val sel = IO(Input(Bool()))
  val in = IO(Input(DspComplex(proto)))
  val out = IO(Output(DspComplex(proto)))

  val feedbackIn = Wire(Valid(DspComplex(proto)))
  val feedback = if (delay > 1) { //delay > 1) {
    ShiftRegisterMem(feedbackIn, delay).bits
  } else {
    ShiftRegister(feedbackIn.bits, delay, en = feedbackIn.valid)
  }

  feedbackIn.valid := en

  when (sel) {
    feedbackIn.bits := feedback - in
  } .otherwise {
    // passthrough, swap
    feedbackIn.bits := in
  }
  when (ShiftRegister(sel, DspContext.current.numAddPipes)) { //, resetData = false.B, en = true.B)) {
    out := feedback context_+ in
  }.otherwise {
    out := ShiftRegister(feedback, DspContext.current.numAddPipes)
  }
}

class BF2II[T <: Data : Ring](proto: T, val delay: Int) extends MultiIOModule {
  requireIsChiselType(proto)
  require(delay > 0)

  val en = IO(Input(Bool()))
  val inMultByJ = IO(Input(Bool()))
  val sel = IO(Input(Bool()))
  val in = IO(Input(DspComplex(proto)))
  val out = IO(Output(DspComplex(proto)))

  val scaledIn = Wire(in.cloneType)
  scaledIn.real := in.imag
  scaledIn.imag := -in.real
  val inTwiddled = Mux(sel && inMultByJ, scaledIn, in)
  val feedbackIn = Wire(Valid(DspComplex(proto)))
  val feedback = if (delay > 1) { // (delay > 1) {
    ShiftRegisterMem(feedbackIn, delay).bits
  } else {
    ShiftRegister(feedbackIn.bits, delay, en = feedbackIn.valid)
  }

  feedbackIn.valid := en // ShiftRegister(en, DspContext.current.numAddPipes, resetData = false.B, en = true.B)
  when (sel) {
    feedbackIn.bits := feedback - inTwiddled
  } .otherwise {
    feedbackIn.bits := inTwiddled
  }
  when (ShiftRegister(sel, DspContext.current.numAddPipes)) { //, resetData = false.B, en = true.B)) {
    out := feedback context_+ inTwiddled
  }.otherwise {
    out := ShiftRegister(feedback, DspContext.current.numAddPipes)
  }
}

class R22Stage[T <: Data : Ring](proto:T, val n: Int) extends MultiIOModule {
  requireIsChiselType(proto)
  require(n > 2)
  require(isPow2(n))

  val en = IO(Input(Bool()))
  val in = IO(Input(DspComplex(proto)))
  val ctrl = IO(Input(UInt(2.W)))
  val protoOut = chiselTypeOf(in.real context_+ in.real)
  val out = IO(Output(DspComplex(protoOut)))

  val stage1 = Module(new BF2I(proto, n / 2))
  val stage2 = Module(new BF2II(protoOut, n / 4))

  stage1.in := in
  stage1.en := en
  stage1.sel := ctrl(1)

  stage2.in := stage1.out
  stage2.en := ShiftRegister(en, DspContext.current.numAddPipes, resetData = false.B, en = true.B)
  stage2.sel := ShiftRegister(ctrl(0), DspContext.current.numAddPipes)
  stage2.inMultByJ := ShiftRegister(!ctrl(1) && ctrl(0), DspContext.current.numAddPipes)

  out := stage2.out
}

class SDFInputManager[T <: Data](val n: Int, val proto: T) extends MultiIOModule {
  require(n > 0)
  requireIsChiselType(proto)

  val in = IO(Flipped(Decoupled(proto)))
  val in_last = IO(Input(Bool()))

  val out = IO(Decoupled(proto))
  val out_last = IO(Output(Bool()))

  val cnt = RegInit(0.U(log2Ceil(n).W))
  val last = RegInit(false.B)

  out <> in
  out_last := in_last && !last
  when (out.fire()) {
    cnt := cnt +% 1.U
  }

  when (in.fire() && in_last) {
    last := true.B
  }

  // this flushes the previous
  when (last) {
    out.bits := 0.U.asTypeOf(proto)
    out.valid := true.B
    in.ready := false.B
    when (out.fire() && cnt === (n - 1).U) {
      last := false.B
    }
  }
}

class SDFOutputManager[T <: Data](val n: Int, val proto: T) extends MultiIOModule {
  require(n > 0)
  requireIsChiselType(proto)

  val in = IO(Flipped(Decoupled(proto)))
  val in_last = IO(Input(Bool()))

  val out = IO(Decoupled(proto))
  val out_last = IO(Output(Bool()))

  object State extends ChiselEnum {
    // sLast is not the "last" state in the state machine,
    // it is the state you go to when in_last is asserted
    val sLast, sDrop, sPass = Value
  }

  val state = RegInit(State.sDrop)
  val cnt = RegInit(n.U)
  var drop = WireInit(false.B)

  when (in.fire() && cnt > 0.U) {
    cnt := cnt -% 1.U
  }

  // unless we are dropping, pass through
  out <> in
  when (drop) {
    in.ready := true.B
    out.valid := false.B
  }
  out_last := false.B

  when (state === State.sLast) {
    when (in.fire() && cnt === 0.U) {
      cnt := n.U
      state := State.sDrop
      out_last := true.B
    }
  }.elsewhen (state === State.sDrop) {
    drop := true.B
    when (in.fire() && cnt === 0.U) {
      state := State.sPass
    }
  } .elsewhen (state === State.sPass) {
    // stay in here until you see in_last
  }

  when (in.valid && in_last) {
    drop := false.B
    state := State.sLast
    cnt := n.U
  }
}

@chiselName
class R22SDF[T <: Data : Ring : ConvertableTo](val n: Int, val protoIn: T, val protoOut: T, val protoTwiddle: T)
extends MultiIOModule {
  requireIsChiselType(protoIn)
  requireIsChiselType(protoOut)
  requireIsChiselType(protoTwiddle)
  require(n > 2)
  require(isPow2(n))
  val logn = log2Ceil(n)
  require(logn % 2 == 0, "n must be a power of 4")

  val in = IO(Flipped(Decoupled(DspComplex(protoIn, protoIn))))
  val in_last = IO(Input(Bool()))
  val out = IO(Decoupled(DspComplex(protoOut, protoOut)))
  val out_last = IO(Output(Bool()))

  val inputManager = Module(new SDFInputManager(n - 1, DspComplex(protoIn, protoIn)))
  inputManager.in <> in
  inputManager.in_last := in_last
  val outputManager = Module(new SDFOutputManager(n - 1, DspComplex(protoOut, protoOut)))
  out <> outputManager.out
  out_last := outputManager.out_last

  val cnt = RegInit(0.U(logn.W))

  when (inputManager.out.fire()) {
    cnt := cnt +% 1.U
  }

  val complexMulLatency = DspContext.current.complexMulPipe

  //                input,         en,   last, ctrl, proto
  type StageInfo = (DspComplex[T], Bool, Bool, UInt, T)
  def makeStage(stageInfo: StageInfo, logStage: Int): StageInfo = {
    val (input, en, last, ctrl, proto) = stageInfo
    val stageN = 1 << (logn - logStage)
    val lastStage = stageN == 4
    val outputLatency = 2 * DspContext.current.numAddPipes + (if (lastStage) 0 else complexMulLatency)

    val stage = Module(new R22Stage(proto, stageN))
    stage.in := input
    stage.en := en
    stage.ctrl := ctrl.head(2)
    println(s"stage $stageN")

    val lastOut = if (!lastStage) {
      val twiddleIdxs =
        Seq.fill(stageN / 4)(0) ++
        Seq.tabulate(stageN / 4)(i => i * 2) ++
        Seq.tabulate(stageN / 4)(i => i) ++
        Seq.tabulate(stageN / 4)(i => i * 3) // ++
      val uniqueTwiddleIdxs = twiddleIdxs.distinct.sorted
      val uniqueTwiddleTable = VecInit(uniqueTwiddleIdxs.map(t =>
        DspComplex.wire(
          real = ConvertableTo[T].fromDoubleWithFixedWidth( math.cos(2 * math.Pi * t / stageN), protoTwiddle),
          imag = ConvertableTo[T].fromDoubleWithFixedWidth(-math.sin(2 * math.Pi * t / stageN), protoTwiddle)
        )))
      val twiddleIdxTable = VecInit(twiddleIdxs.map(i => {
        ( (uniqueTwiddleIdxs.indexOf(i) /*+ stageN / 2*/) /*% stageN*/).U
      }))

      val twiddleCnt = ShiftRegister(ctrl + (stageN / 4).U, 2 * DspContext.current.numAddPipes)
      val twiddle = uniqueTwiddleTable(twiddleIdxTable(twiddleCnt))

      stage.out context_* twiddle
    } else {
      stage.out
    }

    (
      lastOut,
      ShiftRegister(en, outputLatency, resetData = false.B, en = true.B),
      ShiftRegister(last, outputLatency),
      ShiftRegister(ctrl.tail(2), outputLatency, resetData = 0.U, en = true.B),
      chiselTypeOf(stage.out.real)
    )
  }

  val initStageInfo = (inputManager.out.bits, inputManager.out.fire(), inputManager.out_last, cnt, protoIn)
  val (outBits, outFire, outLast, outCnt, _) = (0 until logn by 2).foldLeft(initStageInfo)(makeStage)

  //             (1 add per BF stage)                     (1 mul every other stage, also excluding the last two)
  val latency = (DspContext.current.numAddPipes) * logn + complexMulLatency * ((logn - 2) / 2)
  println(s"latency = $latency, numAdd = ${DspContext.current.numAddPipes}, numMul = ${DspContext.current.numMulPipes}")
  // entries has + 1 because does not input does not flow through to output in single cycle
  val outQueue = Module(new Queue(DspComplex(protoOut), entries = latency + 1))
  val outLastQueue = Module(new Queue(Bool(), entries = latency + 1))
  outQueue.io.enq.bits := outBits
  outQueue.io.enq.valid := outFire
  assert(!outFire || outQueue.io.enq.ready, "Output dropped because output queue full")

  outLastQueue.io.enq.bits := outLast
  outLastQueue.io.enq.valid := outFire
  assert(!outFire || outLastQueue.io.enq.ready, "Last dropped because output queue full")
  assert(outQueue.io.deq.valid === outLastQueue.io.deq.valid)

  val inFlight = RegInit(0.U(log2Ceil(latency + 2).W))
  inFlight := inFlight +% inputManager.out.fire() -% outFire
  assert(inFlight > 0.U || inputManager.out.fire() || !outFire, "Underflow on the inFlight counter")

  inputManager.out.ready := inFlight +% outQueue.io.count < (latency + 1).U
  outputManager.in <> outQueue.io.deq
  outputManager.in_last := outLastQueue.io.deq.bits
  outLastQueue.io.deq.ready := outputManager.in.ready

  out <> outputManager.out
  out_last := outputManager.out_last
}

val delays = (1 to nStages).map { n >> _ }


package ofdm

import chisel3._
import chisel3.internal.requireIsChiselType
import chisel3.util.log2Ceil

case class PacketizerIO[T <: Data](proto: T, size: Int) extends Bundle {
  requireIsChiselType(proto)
  val in  = Flipped(IrrevocableLast(proto))
  val out = IrrevocableLast(proto)

  val includeMask = Input(Vec(size, Bool()))
}

class Packetizer[T <: Data](proto: T, size: Int) extends Module {
  val io = IO(PacketizerIO(proto, size))

  val count = RegInit(UInt(log2Ceil(size).W), 0.U)
  val nextCount = Wire(UInt())
  count := nextCount

  when (io.in.fire()) {
    nextCount := count + 1.U

    nextCount := 0.U
  }

  io.out.valid := io.in.fire() && io.includeMask(count)
  io.out.bits  := io.in.bits
  io.out.last  := nextCount === 0.U
}

    when (count + 1.U === size.U) {
      nextCount := 0.U
    }


    if (in.length == 1) {
      return in.head
    }
    if (in.length == 2) {
      return func(in.head, in(1))
    }
    if (in.length % 2 == 0) {
      val withIdxs = in.zipWithIndex
      val evens = withIdxs.filter{case (_, idx) => idx % 2 == 0}.map(_._1)
      val odds  = withIdxs.filter{case (_, idx) => idx % 2 != 0}.map(_._1)
      val evenOddPairs: Seq[(V, V)] = evens zip odds
      TreeReduce(evenOddPairs.map(x => func(x._1, x._2)), func)
    } else {
      TreeReduce(Seq(in.head, TreeReduce(in.drop(1), func)), func)
    }
  }
}

def apply[V](in: Seq[V], func: (V, V) => V): V = {


  behavior of "Params"

  it should "compute a good schedule" in {

    for (e <- CCSDS.params64x128.schedule) {
      println(e.entries.mkString(", "))
    }
  }

  behavior of "BPDecoder"

  it should "decode small CCSDS (128, 64) code" in {
    BPDecoderTester(FixedPoint(6.W, 2.BP), CCSDS.params64x128, nTrials = 2000, ebn0 = Seq(0.0, 1.0, 2.0, 3.0, 10.0)) should be (true)
  }

  it should "decode the medium CCSDS (256, 128) code" in {
    BPDecoderTester(FixedPoint(6.W, 2.BP), CCSDS.params128x256, nTrials = 500, ebn0 = Seq(3.0)) should be (true)
  }

  it should "decode larger CCSDS (512, 256) code" in {
    BPDecoderTester(FixedPoint(6.W, 2.BP), CCSDS.params256x512, nTrials = 500, ebn0 = Seq(3.0)) should be (true)
  }
}

    it should s"work for n = $i" in {
      ShifterTester(i) should be (true)
    }


package ofdm
package fft

import dsptools.DspTester
import breeze.math.Complex
import chisel3._
import dsptools.numbers._

/*
 * Contains useful helper functions for testers
 */
trait HasTesterUtil[T <: Module] extends DspTester[T] {

  /*
   * Waits to see if a signal is asserted. If more than the allowed number
   * of cycles is passed while waiting, fail the test.
   */
  def wait_for_assert(signal: Bool, maxCyclesWait: Int) {
    require(maxCyclesWait > 0, "maximum number of cycles to wait must be positive")
    var cyclesWaiting = 0
    while (!peek(signal) && cyclesWaiting < maxCyclesWait) {
      cyclesWaiting += 1

      step(1)
    }
  }

  // Poke the individual elements of a Vec of DspComplex
  def poke_complex_seq[U <: Data](sig_vec: Vec[DspComplex[U]], stim_seq: Seq[Complex]) {
    stim_seq.zipWithIndex.foreach { case (value, index) => poke(sig_vec(index), value) }
  }

  // Inspect and expect the individual elements of a Vec of DspComplex
  def expect_complex_seq[U <: Data](sig_vec: Vec[DspComplex[U]], exp_seq: Seq[Complex]) {
    assert(sig_vec.length == exp_seq.length)

    for ((e, s) <- exp_seq.zip(sig_vec)) {
      expect(s, e)
    }
  }

}

if (cyclesWaiting >= maxCyclesWait) { expect(false, "waited for input too long") }


package cc.xiangshan.openncb

import org.chipsalliance.cde.config.Parameters

trait WithNCBParameters {

    implicit val p: Parameters


package cc.xiangshan.openncb.axi.channel

import chisel3._
import chisel3.util._
import org.chipsalliance.cde.config.Parameters
import cc.xiangshan.openncb.axi.bundle._

/*
* AXI AW Channel.
*/
class AXI4ChannelAW[+T <: AXI4BundleAW](gen: T) extends AbstractAXI4Channel[T](gen)

object AXI4ChannelAW {

    def apply[T <: AXI4BundleAW](gen: T) = new AXI4ChannelAW(gen)

}

// Master AW Channel.
object AXI4ChannelMasterAW {

    def apply[T <: AXI4BundleAW](gen: T) = AXI4ChannelAW(gen)

    def apply()(implicit p: Parameters) = AXI4ChannelAW()
}

// Slave AW Channel.
object AXI4ChannelSlaveAW {

    def apply[T <: AXI4BundleAW](gen: T) = Flipped(AXI4ChannelAW(gen))

    def apply()(implicit p: Parameters) = Flipped(AXI4ChannelAW())
}

def apply()(implicit p: Parameters) = new AXI4ChannelAW(new AXI4BundleAW)


package cc.xiangshan.openncb.chi

import chisel3._
import chisel3.util.Cat
import cc.xiangshan.openncb.chi.bundle.CHIBundleDAT
import cc.xiangshan.openncb.chi.bundle.AbstractCHIBundle
import freechips.rocketchip.util.SeqToAugmentedSeq
import freechips.rocketchip.util.DataToAugmentedData

package object channel {

    /*
    * Connectors for CHI Channels and CHI Raw Channels
    */
    implicit class connectRawChannelFrom(dst: CHIRawChannel) {

        def :<<=[T <: Data](src: AbstractCHIChannel[T]) = {

            require(dst.channelType == src.channelType,
                s"channel type mismatch: " +
                s"${dst.channelType.canonicalName} :<= ${src.channelType.canonicalName}")

            if (dst.flit.isWidthKnown)
                require(dst.flit.getWidth == src.flit.getWidth,
                    s"width mismatch in raw channel connection: " +
                    s"(dst = ${dst.flit.getWidth}) :<= (src = ${src.flit.getWidth})")

            dst.flitpend    := src.flitpend
            dst.flitv       := src.flitv
            dst.flit        := Cat(src.flit.getElements.map(_.asUInt))
            src.lcrdv       := dst.lcrdv
        }
    }

    implicit class connectRawChannelTo(src: CHIRawChannel) {

        def :>>=[T <: Data](dst: AbstractCHIChannel[T]) = {

            require(dst.channelType == src.channelType,
                s"channel type mismatch: " +

            if (dst.flit.isWidthKnown)
                require(dst.flit.getWidth == src.flit.getWidth,
                    s"width mismatch in raw channel connection: " +
                    s"(dst = ${dst.flit.getWidth}) :>= (src = ${src.flit.getWidth})")

            dst.flitpend    := src.flitpend
            dst.flitv       := src.flitv
            src.lcrdv       := dst.lcrdv

            var scalaLsb = 0
            dst.flit.getElements.reverse.foreach(e => {
                e := src.flit(scalaLsb + e.getWidth - 1, scalaLsb)
                scalaLsb += e.getWidth
            })
        }
    }
}


package cc.xiangshan.openncb.logical

import chisel3._
import chisel3.util._
import org.chipsalliance.cde.config.Parameters
import org.chipsalliance.cde.config.Field
import cc.xiangshan.openncb.WithNCBParameters
import cc.xiangshan.openncb.chi.WithCHIParameters
import cc.xiangshan.openncb.chi.field.CHIFieldSize
import cc.xiangshan.openncb.debug.DebugBundle
import cc.xiangshan.openncb.debug.DebugSignal
import cc.xiangshan.openncb.debug.DebugElement
import cc.xiangshan.openncb.util.ParallelMux
import cc.xiangshan.openncb.util.XZBarrier
import cc.xiangshan.openncb.util.ValidMux

/*
* NCB Address CAM for transaction address-order maintainence.
*/
object NCBOrderAddressCAM {

    case class PublicParameters (
    )

    case object PublicParametersKey extends Field[PublicParameters]
}

class NCBOrderAddressCAM(implicit val p: Parameters)
        extends Module with WithCHIParameters
                       with WithNCBParameters {

    // public parameters
    val param   = p.lift(NCBOrderAddressCAM.PublicParametersKey)
        .getOrElse(new NCBOrderAddressCAM.PublicParameters)

    // local parameters
    val paramMaskWidth      = log2Up(CHIFieldSize.Size64B.sizeInBytes)

    def paramCAMCapacity    = paramNCB.outstandingDepth

    /*
    * Port I/O: Allocate
    *
    * @io input     en      : Allocation Enable.
    * @io input     strb    : Allocation Strobe, one-hot addressing.
    * @io input     mask    : Allocation Address Mask, masking lower bits of maximum
    *                         address alignment width for overlapped comparsion.
    * @io input     addr    : Address Value, content-addressing.
    */
    class AllocatePort extends Bundle {
        val en          = Input(Bool())
        val strb        = Input(Vec(paramCAMCapacity, Bool()))
        val mask        = Input(UInt(paramMaskWidth.W))
        val addr        = Input(UInt(paramCHI.reqAddrWidth.W))
    }

    /*
    * Port I/O: Free
    *
    * @io input     strb    : Free Strobe, one-hot addressing.
    */
    class FreePort extends Bundle {
        val strb        = Input(Vec(paramCAMCapacity, Bool()))
    }

    /*
    * Port I/O: Query
    *
    * @io input     mask        : Query Address Mask, masking lower bits of maximum
    *                             address alignment width for overlapped comparsion.
    * @io input     addr        : Query Address, content-addressing.
    * @io output    resultValid : Query Result Valid.
    * @io output    resultIndex : Query Result Index.
    */
    class QueryPort extends Bundle {
        val mask        = Input(UInt(paramMaskWidth.W))
        val addr        = Input(UInt(paramCHI.reqAddrWidth.W))
        val resultValid = Output(Bool())
        val resultIndex = Output(UInt(paramNCB.outstandingIndexWidth.W))
    }

    /*
    * Module I/O
    */
    val io = IO(new Bundle {
        // allocate port
        val allocate        = new AllocatePort

        // free port
        val free            = new FreePort

        // query port
        val query           = new QueryPort
    })

    // CAM Registers
    class CAMEntry extends Bundle {
        val valid       = Bool()
        val mask        = UInt(paramMaskWidth.W)
        val addr        = UInt(paramCHI.reqAddrWidth.W)
    }

    protected val regCAM  = RegInit(init = VecInit(Seq.fill(paramCAMCapacity){
        val resetValue  = Wire(new CAMEntry)
        resetValue.valid    := false.B
        resetValue.mask     := DontCare
        resetValue.addr     := DontCare
        resetValue
    }))

    // CAM comparators
    def funcCompare(a: UInt, b: UInt, mask: UInt) = {

        require(a.isWidthKnown && b.isWidthKnown && mask.isWidthKnown)
        require(a.getWidth == b.getWidth)

        val wireUpperA  = a.head(a.getWidth - mask.getWidth)
        val wireUpperB  = b.head(b.getWidth - mask.getWidth)

        val logicLowerA = a(mask.getWidth - 1, 0) & mask
        val logicLowerB = a(mask.getWidth - 1, 0) & mask

        (wireUpperA === wireUpperB) && (logicLowerA === logicLowerB)
    }

    // CAM update logic
    regCAM.zipWithIndex.foreach({ case (entry, i) => {

        // CAM entry allocate and update
        when (io.allocate.en) {
            when (io.allocate.strb(i)) {
                entry.valid := true.B
                entry.mask  := io.allocate.mask
                entry.addr  := io.allocate.addr

            }.elsewhen (funcCompare(entry.addr, io.allocate.addr, entry.mask & io.allocate.mask)) {
                entry.valid := false.B
            }
        }

        // CAM entry free
        when (io.free.strb(i)) {
            entry.valid := false.B
        }
    }})

    // CAM query logic
    val logicCAMHit = regCAM.map(entry => {
        ValidMux(entry.valid,
            funcCompare(entry.addr, io.query.addr, entry.mask & io.query.mask))
    })

    io.query.resultValid    := VecInit(logicCAMHit).asUInt.orR
    io.query.resultIndex    := ParallelMux(logicCAMHit.zipWithIndex.map({ case(entry, i) => {
        (entry, i.U(paramNCB.outstandingIndexWidth.W))
    }}))

    // assertions & debugs
    /*
    * Port I/O: Debug
    */
    class DebugPort extends DebugBundle {
        val AllocateNotOneHot           = Output(Bool())
        val QueryResultMultipleHit      = Output(Bool())
        val DoubleAllocation            = Output(Vec(paramCAMCapacity, Bool()))
    }

    @DebugSignal
    val debug   = IO(new DebugPort)

    @DebugElement
    protected val debugRegCAMFreeable   = RegInit(
        init = VecInit(Seq.fill(paramCAMCapacity)(false.B)))

    debugRegCAMFreeable.zipWithIndex.foreach({ case (entry, i) => {

        when (io.free.strb(i)) {
            entry   := false.B
        }

        when (io.allocate.en & io.allocate.strb(i)) {
            entry   := true.B
        }
    }})

    /*
    * @assertion AllocateNotOneHot
    *   The allocation strobe must be one-hot. Only one exactly entry was allowed to be
    *   allocated at a time.
    */
    debug.AllocateNotOneHot := io.allocate.en && PopCount(io.allocate.strb) =/= 1.U
    assert(!debug.AllocateNotOneHot,
        "multiple or zero allocation")

    /*
    * @assertion QueryResultMultipleHit
    *   On query, no more than one entry was allowed to be hit.
    */
    private val debugWireQueryHit = regCAM.map(entry => {
        XZBarrier(entry.valid, funcCompare(entry.addr, io.query.addr, entry.mask & io.query.mask))
    })
    debug.QueryResultMultipleHit := PopCount(debugWireQueryHit) > 1.U
    assert(!debug.QueryResultMultipleHit,
        "multiple query result hit")

    /*
    * @assertion DoubleAllocation
    *   A yet allocated entry was not allowed to be allocated again.
    */
    (0 until paramCAMCapacity).foreach(i => {
        debug.DoubleAllocation(i) := io.allocate.en && io.allocate.strb(i) && (regCAM(i).valid || debugRegCAMFreeable(i))
        assert(!debug.DoubleAllocation(i),


package cc.xiangshan.openncb.logical

import chisel3._
import chisel3.util.log2Up
import org.chipsalliance.cde.config.Parameters
import org.chipsalliance.cde.config.Field
import cc.xiangshan.openncb.WithNCBParameters
import cc.xiangshan.openncb.axi.WithAXI4Parameters
import cc.xiangshan.openncb.axi.field.AXI4FieldAxBURST
import cc.xiangshan.openncb.axi.field.AXI4FieldAxSIZE
import cc.xiangshan.openncb.chi.CHIConstants
import cc.xiangshan.openncb.chi.WithCHIParameters
import cc.xiangshan.openncb.util.ParallelMux
import cc.xiangshan.openncb.util.ValidMux
import cc.xiangshan.openncb.debug.DebugBundle
import cc.xiangshan.openncb.debug.DebugSignal

/*
* NCB Transaction Queue.
*/
object NCBTransactionQueue {

    case class PublicParameters (
    )

    case object PublicParametersKey extends Field[PublicParameters]
}

class NCBTransactionQueue(implicit val p: Parameters)
        extends Module with WithAXI4Parameters
                       with WithCHIParameters
                       with WithNCBParameters {

    // public parameters
    val param   = p.lift(NCBTransactionQueue.PublicParametersKey)
        .getOrElse(new NCBTransactionQueue.PublicParameters)

    // local parameters
    protected def paramQueueCapacity                = paramNCB.outstandingDepth
    protected def paramQueueAddressWidth            = paramNCB.outstandingIndexWidth

    protected def paramUpstreamMaxBeatCount         = CHIConstants.CHI_MAX_PACKET_DATA_BITS_WIDTH / paramCHI.dataWidth
    protected def paramUpstreamMaxBeatCountWidth    = log2Up(paramUpstreamMaxBeatCount)

    protected def paramDownstreamMaxBeatCount       = CHIConstants.CHI_MAX_PACKET_DATA_BITS_WIDTH / paramAXI4.dataWidth
    protected def paramDownstreamMaxBeatCountWidth  = log2Up(paramDownstreamMaxBeatCount)

    /*
    * Bundle definitions
    */
    trait TraitTransactionOpElement {
        val valid           = Bool()

        def ready: Bool = valid
    }

    class TransactionOpCHIComp extends Bundle with TraitTransactionOpElement {
        val barrier         = new Bundle {
            val CHICancelOrAXIBresp = Bool()
        }

        override def ready: Bool = valid & !barrier.asUInt.orR
    }

    class TransactionOpCHIDBIDResp extends Bundle with TraitTransactionOpElement

    class TransactionOpCHICompDBIDResp extends Bundle with TraitTransactionOpElement

    class TransactionOpCHIReadReceipt extends Bundle with TraitTransactionOpElement {
        val barrier         = new Bundle {
            val AXIARready          = Bool()
        }

        override def ready: Bool = valid & !barrier.asUInt.orR
    }

    class TransactionOpCHICompData extends Bundle with TraitTransactionOpElement

    class TransactionOpAXIWriteAddress extends Bundle with TraitTransactionOpElement {
        val barrier         = new Bundle {
            val CHIWriteBackData    = Bool()
        }

        override def ready: Bool = valid & !barrier.asUInt.orR
    }

    class TransactionOpAXIWriteData extends Bundle with TraitTransactionOpElement

    class TransactionOpAXIWriteResponse extends Bundle with TraitTransactionOpElement

    class TransactionOpAXIReadAddress extends Bundle with TraitTransactionOpElement

    class TransactionOpAXIReadData extends Bundle with TraitTransactionOpElement

    class TransactionOp extends Bundle {
        // CHI ops
        val chi             = new Bundle {
            //
            val Comp            = new TransactionOpCHIComp
            val DBIDResp        = new TransactionOpCHIDBIDResp
            val CompDBIDResp    = new TransactionOpCHICompDBIDResp
            val ReadReceipt     = new TransactionOpCHIReadReceipt
            //
            val CompData        = new TransactionOpCHICompData

            //
            def valid           = VecInit(
                getElements.map(_.asInstanceOf[TraitTransactionOpElement].valid)
            ).asUInt.orR
        }

        // AXI ops
        val axi             = new Bundle {
            //
            val WriteAddress    = new TransactionOpAXIWriteAddress
            val WriteData       = new TransactionOpAXIWriteData
            val WriteResponse   = new TransactionOpAXIWriteResponse
            //
            val ReadAddress     = new TransactionOpAXIReadAddress
            val ReadData        = new TransactionOpAXIReadData

            //
            def valid       = VecInit(
                getElements.map(_.asInstanceOf[TraitTransactionOpElement].valid)
            ).asUInt.orR
        }
    }

    //
    class TransactionInfo extends Bundle {
        val QoS             = UInt(paramCHI.reqQoSWidth.W)
        val TgtID           = UInt(paramCHI.reqTgtIDWidth.W)
        val SrcID           = UInt(paramCHI.reqSrcIDWidth.W)
        val TxnID           = UInt(paramCHI.reqTxnIDWidth.W)
        val ReturnNID       = UInt(paramCHI.reqReturnNIDWidth.W)
        val ReturnTxnID     = UInt(paramCHI.reqReturnTxnIDWidth.W)
    }

    //
    class TransactionOperandCHI extends Bundle {
        val Addr            = UInt(paramCHI.reqAddrWidth.W)
        //
        val WriteFull       = Bool()
        val WritePtl        = Bool()
        //
        val ReadRespErr     = UInt(paramCHI.rspRespErrWidth.W)
        val WriteRespErr    = UInt(paramCHI.datRespErrWidth.W)
        //
        val Critical        = Vec(paramUpstreamMaxBeatCount, Bool())
        val Count           = UInt(paramUpstreamMaxBeatCountWidth.W)
    }

    class TransactionOperandAXI extends Bundle {
        val Addr            = UInt(paramAXI4.addrWidth.W)
        val Burst           = UInt(AXI4FieldAxBURST.width.W)
        val Size            = UInt(AXI4FieldAxSIZE.width.W)
        val Len             = UInt(8.W)
        val Device          = Bool()
        //
        val Critical        = Vec(paramDownstreamMaxBeatCount, Bool())
        val Count           = UInt(paramDownstreamMaxBeatCountWidth.W)
    }

    class TransactionOperand extends Bundle {
        // CHI operands
        val chi             = new TransactionOperandCHI

        // AXI operands
        val axi             = new TransactionOperandAXI
    }

    //
    class TransactionOrder extends Bundle {
        val valid           = Bool()
        val index           = UInt(paramQueueAddressWidth.W)
    }
    /**/

    //
    class QueueEntry extends Bundle {
        // operation bit entires
        val op              = new TransactionOp

        // transaction info entries
        val info            = new TransactionInfo

        // transaction operand entries
        val operand         = new TransactionOperand

        // transaction order maintainence entries
        val order           = new TransactionOrder
    }

    /*
    * Port I/O: Transaction Allocation
    */
    class AllocatePort extends Bundle {
        // allocation enable
        val en              = Input(Bool())

        // allocation target strobe
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))

        // allocation transaction bits
        val bits            = new Bundle {
            // operation bits
            val op              = Input(new TransactionOp)

            // transaction infos
            val info            = Input(new TransactionInfo)

            // transaction operands
            val operand         = Input(new TransactionOperand)

            // transaction order maintainence
            val order           = Input(new TransactionOrder)
        }
    }

    /*
    * Port I/O: Transaction Free
    */
    class FreePort extends Bundle {
        // free strobe
        val strb            = Output(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Upstream Query (for RXDAT)
    */
    class UpstreamQueryPort extends Bundle {
        val en              = Input(Bool())
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val result          = new Bundle {
            val valid           = Output(Bool())
            val WriteFull       = Output(Bool())
            val WritePtl        = Output(Bool())
            val ReadRespErr     = Output(UInt(paramCHI.rspRespErrWidth.W))
            val WriteRespErr    = Output(UInt(paramCHI.datRespErrWidth.W))
        }
    }

    /*
    * Port I/O: Upstream Cancel Update (for RXDAT)
    */
    class UpstreamCancelPort extends Bundle {
        val en              = Input(Bool())
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Upstream Write Data Update (for RXDAT)
    */
    class UpstreamWriteDataPort extends Bundle {
        val en              = Input(Bool())
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Upstream RSP Op Valid (for TXRSP)
    */
    class UpstreamRspOpValidPort extends Bundle {
        val valid           = Output(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Upstream RSP Op Read (for TXRSP)
    */
    class UpstreamRspOpReadPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = new Bundle {
            val Comp            = Output(Bool())
            val DBIDResp        = Output(Bool())
            val CompDBIDResp    = Output(Bool())
            val ReadReceipt     = Output(Bool())
        }
    }

    /*
    * Port I/O: Upstream RSP Op Done (for TXRSP)
    */
    class UpstreamRspOpDonePort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = new Bundle {
            val Comp            = Input(Bool())
            val DBIDResp        = Input(Bool())
            val CompDBIDResp    = Input(Bool())
            val ReadReceipt     = Input(Bool())
        }
    }

    /*
    * Port I/O: Upstream RSP Info Read (for TXRSP)
    */
    class UpstreamRspInfoReadPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = Output(new TransactionInfo)
    }

    /*
    * Port I/O: Upstream RSP Operand Read (for TXRSP)
    */
    class UpstreamRspOperandReadPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = Output(new TransactionOperandCHI)
    }

    /*
    * Port I/O: Upstream DAT Op Valid (for TXDAT)
    */
    class UpstreamDatOpValidPort extends Bundle {
        val valid           = Output(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = new Bundle {
            val Critical        = Output(Vec(paramNCB.outstandingDepth, Vec(paramUpstreamMaxBeatCount, Bool())))
        }
    }

    /*
    * Port I/O: Upstream DAT Op Read (for TXDAT)
    */
    class UpstreamDatOpReadPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = new Bundle {
            val CompData        = new Bundle {
                val valid           = Output(Bool())
            }
        }
    }

    /*
    * Port I/O: Upstream DAT Op Done (for TXDAT)
    */
    class UpstreamDatOpDonePort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = new Bundle {
            val CompData        = Input(Bool())
        }
    }

    /*
    * Port I/O: Upstream DAT Info Read (for TXDAT)
    */
    class UpstreamDatInfoReadPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = Output(new TransactionInfo)
    }

    /*
    * Port I/O: Upstream DAT Operand Read (for TXDAT)
    */
    class UpstreamDatOperandReadPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = Output(new TransactionOperandCHI)
    }

    /*
    * Port I/O: Upstream DAT Operand Write (for TXDAT)
    */
    class UpstreamDatOperandWritePort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = new Bundle {
            val Critical        = Input(Vec(paramUpstreamMaxBeatCount, Bool()))
            val Count           = Input(UInt(paramUpstreamMaxBeatCountWidth.W))
        }
    }

    /*
    * Port I/O: Downstream AW Op Valid Read
    */
    class DownstreamAWOpValidPort extends Bundle {
        val valid           = Output(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Downstream AW Op Point of No Return
    */
    class DownstreamAWOpPoNRPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Downstream AW Op Done
    */
    class DownstreamAWOpDonePort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Downstream AW Info Read
    */
    class DownstreamAWInfoReadPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = Output(new TransactionInfo)
    }

    /*
    * Port I/O: Downstream AW Operand Read
    */
    class DownstreamAWOperandReadPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = Output(new TransactionOperandAXI)
    }

    /*
    * Port I/O: Downstream W Op Point of No Return
    */
    class DownstreamWOpPoNRPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Downstream W Op Done
    */
    class DownstreamWOpDonePort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Downstream W Operand Read
    */
    class DownstreamWOperandReadPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = Output(new TransactionOperandAXI)
    }

    /*
    * Port I/O: Downstream W Operand Write
    */
    class DownstreamWOperandWritePort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = new Bundle {
            val Critical        = Input(Vec(paramDownstreamMaxBeatCount, Bool()))
            val Count           = Input(UInt(paramDownstreamMaxBeatCountWidth.W))
        }
    }

    /*
    * Port I/O: Downstream B Op Done
    */
    class DownstreamBOpDonePort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Downstream B Operand Write
    */
    class DownstreamBOperandWritePort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = new Bundle {
            val WriteRespErr    = Input(UInt(paramCHI.rspRespErrWidth.W))
        }
    }

    /*
    * Port I/O: Downstream AR Op Valid
    */
    class DownstreamAROpValidPort extends Bundle {
        val valid           = Output(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Downstream AR Op Point of No Return
    */
    class DownstreamAROpPoNRPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Downstream AR Op Done
    */
    class DownstreamAROpDonePort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Downstream AR Info Read
    */
    class DownstreamARInfoReadPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = Output(new TransactionInfo)
    }

    /*
    * Port I/O: Downstream AR Operand Read
    */
    class DownstreamAROperandReadPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = Output(new TransactionOperandAXI)
    }

    /*
    * Port I/O: Downstream R Op Done
    */
    class DownstreamROpDonePort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
    }

    /*
    * Port I/O: Downstream R Operand Read
    */
    class DownstreamROperandReadPort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = Output(new TransactionOperandAXI)
    }

    /*
    * Port I/O: Downstream R Operand AXI Write
    */
    class DownstreamROperandAXIWritePort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = new Bundle {
            val Critical        = Input(Vec(paramDownstreamMaxBeatCount, Bool()))
            val Count           = Input(UInt(paramDownstreamMaxBeatCountWidth.W))
        }
    }

    /*
    * Port I/O: Downstream R Operand CHI Write
    */
    class DownstreamROperandCHIWritePort extends Bundle {
        val strb            = Input(Vec(paramNCB.outstandingDepth, Bool()))
        val bits            = new Bundle {
            val ReadRespErr     = Input(UInt(paramCHI.datRespErrWidth.W))
        }
    }

    /*
    * Module I/O
    */
    val io = IO(new Bundle {
        // free output port
        val free                    = new FreePort

        // allocate port (for RXREQ)
        val allocate                = new AllocatePort

        // upstream ports (for RXDAT)
        val upstreamRxDat           = new Bundle {
            val query                   = new UpstreamQueryPort
            val cancel                  = new UpstreamCancelPort
            val writeData               = new UpstreamWriteDataPort
        }

        // upstream ports (for TXRSP)
        val upstreamTxRsp           = new Bundle {
            val opValid                 = new UpstreamRspOpValidPort
            val opRead                  = new UpstreamRspOpReadPort
            val opDone                  = new UpstreamRspOpDonePort
            val infoRead                = new UpstreamRspInfoReadPort
            val operandRead             = new UpstreamRspOperandReadPort
        }

        // upstream ports (for TXDAT)
        val upstreamTxDat           = new Bundle {
            val opValid                 = new UpstreamDatOpValidPort
            val opRead                  = new UpstreamDatOpReadPort
            val opDone                  = new UpstreamDatOpDonePort
            val infoRead                = new UpstreamDatInfoReadPort
            val operandRead             = new UpstreamDatOperandReadPort
            val operandWrite            = new UpstreamDatOperandWritePort
        }

        // downstream ports (for AXI AW)
        val downstreamAw            = new Bundle {
            val opValid                 = new DownstreamAWOpValidPort
            val opPoNR                  = new DownstreamAWOpPoNRPort
            val opDone                  = new DownstreamAWOpDonePort
            val infoRead                = new DownstreamAWInfoReadPort
            val operandRead             = new DownstreamAWOperandReadPort
        }

        // downstream ports (for AXI W)
        val downstreamW             = new Bundle {
            val opPoNR                  = new DownstreamWOpPoNRPort
            val opDone                  = new DownstreamWOpDonePort
            val operandRead             = new DownstreamWOperandReadPort
            val operandWrite            = new DownstreamWOperandWritePort
        }

        // downstream ports (for AXI B)
        val downstreamB             = new Bundle {
            val opDone                  = new DownstreamBOpDonePort
            val operandWrite            = new DownstreamBOperandWritePort
        }

        // downstream ports (for AXI AR)
        val downstreamAr            = new Bundle {
            val opValid                 = new DownstreamAROpValidPort
            val opPoNR                  = new DownstreamAROpPoNRPort
            val opDone                  = new DownstreamAROpDonePort
            val infoRead                = new DownstreamARInfoReadPort
            val operandRead             = new DownstreamAROperandReadPort
        }

        // downstream ports (for AXI R)
        val downstreamR             = new Bundle {
            val opDone                  = new DownstreamROpDonePort
            val operandRead             = new DownstreamROperandReadPort
            val operandAXIWrite         = new DownstreamROperandAXIWritePort
            val operandCHIWrite         = new DownstreamROperandCHIWritePort
        }
    })

    /*
    * Transaction Queue Registers
    */
    protected val regQueue  = RegInit(init = VecInit(Seq.fill(paramQueueCapacity){
        val resetValue  = Wire(new Bundle {
            val valid       = Output(Bool())
            val bits        = Output(new QueueEntry)
        })

        resetValue.valid    := false.B
        resetValue.bits     := DontCare
        resetValue.bits.op  := 0.U.asTypeOf(chiselTypeOf(resetValue.bits.op))

        resetValue
    }))

    // entry free logic
    protected val logicEntryFree    = regQueue.map({ entry => {
        entry.valid & !entry.bits.op.chi.valid & !entry.bits.op.axi.valid
    }})

    io.free.strb    := logicEntryFree

    logicEntryFree.zipWithIndex.foreach({ case (free, i) => {
        // free self
        when (free) {
            regQueue(i).valid   := false.B
        }
    }})

    regQueue.zipWithIndex.foreach({ case (entry, i) => {
        // free order relation
        val logicOrderHit = VecInit(logicEntryFree.zipWithIndex.map({ case (free, j) => {
            if (i == j) false.B else free && entry.bits.order.index === j.U
        }})).asUInt.orR

        when(logicOrderHit) {
            entry.bits.order.valid  := false.B
        }
    }})

    // entry allocate logic
    val logicAllocateOrderOrdinal   = VecInit((0 until paramQueueCapacity).map(i => {
        io.allocate.bits.order.index === i.U
    }))

    regQueue.zipWithIndex.foreach({ case (entry, i) => {

        when (io.allocate.en & io.allocate.strb(i)) {

            val logicOrderHit   = VecInit(logicEntryFree.zipWithIndex.map({ case (free, j) => {
                if (i == j) false.B else free && logicAllocateOrderOrdinal(j)
            }})).asUInt.orR

            entry.valid := true.B

            entry.bits.op           := io.allocate.bits.op
            entry.bits.info         := io.allocate.bits.info
            entry.bits.operand      := io.allocate.bits.operand
            entry.bits.order.index  := io.allocate.bits.order.index
            entry.bits.order.valid  := ValidMux(!logicOrderHit, io.allocate.bits.order.valid)
        }
    }})

    // barrier logic: CHI.Op.Comp.CHICancelOrAXIBresp
    regQueue.zipWithIndex.foreach({ case (entry, i) => {

        // on CHI WriteDataCancel
        when (io.upstreamRxDat.cancel.en & io.upstreamRxDat.cancel.strb(i)) {
            // clear CHI domain barriers
            entry.bits.op.chi.Comp.barrier.CHICancelOrAXIBresp  := false.B

            // clear AXI domain write tasks
            entry.bits.op.axi.WriteAddress  .valid := false.B
            entry.bits.op.axi.WriteData     .valid := false.B
            entry.bits.op.axi.WriteResponse .valid := false.B
        }

        // on AXI BRESP
        when (io.downstreamB.opDone.strb(i)) {
            // clear CHI domain barriers
            entry.bits.op.chi.Comp.barrier.CHICancelOrAXIBresp  := false.B
        }
    }})

    // barrier logic: CHI.Op.ReadReceipt.AXIARready
    regQueue.zipWithIndex.foreach({ case (entry, i) => {

        // on AXI ARREADY
        when (io.downstreamAr.opDone.strb(i)) {
            // clear CHI domain barriers
            entry.bits.op.chi.ReadReceipt.barrier.AXIARready    := false.B
        }
    }})

    // barrier logic: AXI.Op.WriteAddress.CHIWriteBackData
    regQueue.zipWithIndex.foreach({ case (entry, i) => {

        // on CHI NonCopyBackWrData
        when (io.upstreamRxDat.writeData.en & io.upstreamRxDat.writeData.strb(i)) {
            // clear AXI domain barriers
            entry.bits.op.axi.WriteAddress.barrier.CHIWriteBackData := false.B
        }
    }})

    // TXRSP op valid output logic
    io.upstreamTxRsp.opValid.valid.zipWithIndex.map({ case (valid, i) => {
        (valid, i, regQueue(i).bits.op, regQueue(i).bits.order)
    }}).foreach({ case (valid, i, entry, order) => {
        valid := VecInit(Seq(
            entry.chi.Comp          .ready,
            entry.chi.DBIDResp      .ready,
            entry.chi.CompDBIDResp  .ready,
            entry.chi.ReadReceipt   .ready
        )).asUInt.orR & !order.valid
    }})

    // TXDAT op valid output logic
    io.upstreamTxDat.opValid.valid.zipWithIndex.map({ case (valid, i) => {
        (valid, i, regQueue(i).bits.op, regQueue(i).bits.order)
    }}).foreach({ case (valid, i, entry, order) => {
        valid := VecInit(Seq(
            entry.chi.CompData      .valid
        )).asUInt.orR & !order.valid
    }})

    io.upstreamTxDat.opValid.bits.Critical.zipWithIndex.map({ case (critical, i) => {
        (critical, regQueue(i).bits.operand.chi.Critical)
    }}).foreach({ case (critical, entry) => {
        critical := entry
    }})

    // AXI AW op valid output logic
    io.downstreamAw.opValid.valid.zipWithIndex.map({ case (valid, i) => {
        (valid, regQueue(i).bits.op, regQueue(i).bits.order)
    }}).foreach({ case (valid, entry, order) => {
        valid := entry.axi.WriteAddress.ready & !order.valid
    }})

    // AXI AR op valid output logic
    io.downstreamAr.opValid.valid.zipWithIndex.map({ case (valid, i) => {
        (valid, regQueue(i).bits.op, regQueue(i).bits.order)
    }}).foreach({ case (valid, entry, order) => {
        valid := entry.axi.ReadAddress.ready & !order.valid
    }})

    // TXRSP op read logic
    io.upstreamTxRsp.opRead.bits := ParallelMux(
        io.upstreamTxRsp.opRead.strb.zipWithIndex.map({ case (strb, i) => {
            (strb, regQueue(i).bits.op)
        }}).map({ case (strb, op) => {

            val valid   = Wire(chiselTypeOf(io.upstreamTxRsp.opRead.bits))

            valid.Comp          := op.chi.Comp          .ready
            valid.DBIDResp      := op.chi.DBIDResp      .ready
            valid.CompDBIDResp  := op.chi.CompDBIDResp  .ready
            valid.ReadReceipt   := op.chi.ReadReceipt   .ready

            (strb, valid)
        }})
    )

    // TXDAT op read logic
    io.upstreamTxDat.opRead.bits := ParallelMux(
        io.upstreamTxDat.opRead.strb.zipWithIndex.map({ case (strb, i) => {
            (strb, regQueue(i).bits.op)
        }}).map({ case (strb, entry) => {

            val op  = Wire(chiselTypeOf(io.upstreamTxDat.opRead.bits))

            op.CompData.valid       := entry.chi.CompData.valid

            (strb, op)
        }})
    )

    // AXI AW op PoNR logic
    regQueue.zipWithIndex.map({ case (entry, i) => {
        (entry, io.downstreamAw.opPoNR.strb(i))
    }}).foreach({ case (entry, strb) => {

        when(strb) {
            entry.bits.op.axi.WriteAddress      .valid := false.B
        }
    }})

    // AXI W op PoNR logic
    regQueue.zipWithIndex.map({ case (entry, i) => {
        (entry, io.downstreamW.opPoNR.strb(i))
    }}).foreach({ case (entry, strb) => {

        when (strb) {
            entry.bits.op.axi.WriteData         .valid := false.B
        }
    }})

    // AXI AR op PoNR logic
    regQueue.zipWithIndex.map({ case (entry, i) => {
        (entry, io.downstreamAr.opPoNR.strb(i))
    }}).foreach({ case (entry, strb) => {

        when (strb) {
            entry.bits.op.axi.ReadAddress       .valid := false.B
        }
    }})

    // TXRSP op done logic
    regQueue.zipWithIndex.map({ case (entry, i) => {
        (entry, io.upstreamTxRsp.opDone.strb(i), io.upstreamTxRsp.opDone.bits)
    }}).foreach({ case (entry, strb, bits) => {

        when (strb) {

            when (bits.Comp) {
                entry.bits.op.chi.Comp          .valid := false.B
            }

            when (bits.DBIDResp) {
                entry.bits.op.chi.DBIDResp      .valid := false.B
            }

            when (bits.CompDBIDResp) {
                entry.bits.op.chi.CompDBIDResp  .valid := false.B
            }

            when (bits.ReadReceipt) {
                entry.bits.op.chi.ReadReceipt   .valid := false.B
            }
        }
    }})

    // TXDAT op done logic
    regQueue.zipWithIndex.map({ case (entry, i) => {
        (entry, io.upstreamTxDat.opDone.strb(i), io.upstreamTxDat.opDone.bits)
    }}).foreach({ case (entry, strb, bits) => {

        when (strb) {

            when (bits.CompData & entry.bits.operand.chi.Count === 0.U) {
                entry.bits.op.chi.CompData      .valid := false.B
            }
        }
    }})

    // AXI B op done logic
    regQueue.zipWithIndex.map({ case (entry, i) => {
        (entry, io.downstreamB.opDone.strb(i))
    }}).foreach({ case (entry, strb) => {

        when (strb) {
            entry.bits.op.axi.WriteResponse     .valid := false.B
        }
    }})

    // AXI R op done logic
    regQueue.zipWithIndex.map({ case (entry ,i) => {
        (entry, io.downstreamR.opDone.strb(i))
    }}).foreach({ case (entry, strb) => {

        when (strb) {
            entry.bits.op.axi.ReadData          .valid := false.B
        }
    }})

    // TXRSP info read logic
    io.upstreamTxRsp.infoRead.bits  := ParallelMux(
        io.upstreamTxRsp.infoRead.strb.zipWithIndex.map({ case (strb, i) => {
            (strb, regQueue(i).bits.info)
        }})
    )

    // TXDAT info read logic
    io.upstreamTxDat.infoRead.bits  := ParallelMux(
        io.upstreamTxDat.infoRead.strb.zipWithIndex.map({ case (strb, i) => {
            (strb, regQueue(i).bits.info)
        }})
    )

    // AXI AW info read logic
    io.downstreamAw.infoRead.bits   := ParallelMux(
        io.downstreamAw.infoRead.strb.zipWithIndex.map({ case (strb, i) => {
            (strb, regQueue(i).bits.info)
        }})
    )

    // AXI AR info read logic
    io.downstreamAr.infoRead.bits   := ParallelMux(
        io.downstreamAr.infoRead.strb.zipWithIndex.map({ case (strb, i) => {
            (strb, regQueue(i).bits.info)
        }})
    )

    // TXRSP operand read logic
    io.upstreamTxRsp.operandRead.bits   := ParallelMux(
        io.upstreamTxDat.operandRead.strb.zipWithIndex.map({ case (strb, i) => {
            (strb, regQueue(i).bits.operand.chi)
        }})
    )

    // TXDAT operand read logic
    io.upstreamTxDat.operandRead.bits   := ParallelMux(
        io.upstreamTxDat.operandRead.strb.zipWithIndex.map({ case (strb, i) => {
            (strb, regQueue(i).bits.operand.chi)
        }})
    )

    // AXI AW operand read logic
    io.downstreamAw.operandRead.bits    := ParallelMux(
        io.downstreamAw.operandRead.strb.zipWithIndex.map({ case (strb, i) => {
            (strb, regQueue(i).bits.operand.axi)
        }})
    )

    // AXI W operand read logic
    io.downstreamW.operandRead.bits     := ParallelMux(
        io.downstreamW.operandRead.strb.zipWithIndex.map({ case (strb, i) => {
            (strb, regQueue(i).bits.operand.axi)
        }})
    )

    // AXI AR operand read logic
    io.downstreamAr.operandRead.bits    := ParallelMux(
        io.downstreamAr.operandRead.strb.zipWithIndex.map({ case (strb, i) => {
            (strb, regQueue(i).bits.operand.axi)
        }})
    )

    // AXI R operand read logic
    io.downstreamR.operandRead.bits     := ParallelMux(
        io.downstreamR.operandRead.strb.zipWithIndex.map({ case (strb, i) => {
            (strb, regQueue(i).bits.operand.axi)
        }})
    )

    // TXDAT operand write logic
    regQueue.zipWithIndex.map({ case (entry, i) => {
        (entry, io.upstreamTxDat.operandWrite.strb(i))
    }}).foreach({ case (entry, strb) => {

        when (strb) {
            entry.bits.operand.chi.Critical := io.upstreamTxDat.operandWrite.bits.Critical
            entry.bits.operand.chi.Count    := io.upstreamTxDat.operandWrite.bits.Count
        }
    }})

    // AXI W operand write logic
    regQueue.zipWithIndex.map({ case (entry, i) => {
        (entry, io.downstreamW.operandWrite.strb(i))
    }}).foreach({ case (entry, strb) => {

        when (strb) {
            entry.bits.operand.axi.Critical := io.downstreamW.operandWrite.bits.Critical
            entry.bits.operand.axi.Count    := io.downstreamW.operandWrite.bits.Count
        }
    }})

    // AXI B operand write logic
    regQueue.zipWithIndex.map({ case (entry, i) => {
        (entry, io.downstreamB.operandWrite.strb(i))
    }}).foreach({ case (entry, strb) => {

        when (strb) {
            entry.bits.operand.chi.WriteRespErr := io.downstreamB.operandWrite.bits.WriteRespErr
        }
    }})

    // AXI R operand write logic
    regQueue.zipWithIndex.map({ case (entry, i) => {
        (entry, io.downstreamR.operandAXIWrite.strb(i))
    }}).foreach({ case (entry, strb) => {

        when (strb) {
            entry.bits.operand.axi.Critical     := io.downstreamR.operandAXIWrite.bits.Critical
            entry.bits.operand.axi.Count        := io.downstreamR.operandAXIWrite.bits.Count
        }
    }})

    regQueue.zipWithIndex.map({ case (entry, i) => {
        (entry, io.downstreamR.operandCHIWrite.strb(i))
    }}).foreach({ case (entry, strb) => {

        when (strb) {
            entry.bits.operand.chi.ReadRespErr  := io.downstreamR.operandCHIWrite.bits.ReadRespErr
        }
    }})
    /**/

    // upstream query logic
    io.upstreamRxDat.query.result := ValidMux(io.upstreamRxDat.query.en,
        ParallelMux(io.upstreamRxDat.query.strb.zip(regQueue.map(entry => {
            val result = Wire(Output(chiselTypeOf(io.upstreamRxDat.query.result)))
            result.valid        := entry.valid
            result.WriteFull    := entry.bits.operand.chi.WriteFull
            result.WritePtl     := entry.bits.operand.chi.WritePtl
            result.ReadRespErr  := entry.bits.operand.chi.ReadRespErr
            result.WriteRespErr := entry.bits.operand.chi.WriteRespErr
            result
        })))
    )

    // assertions & debugs
    /*
    * Port I/O: Debug
    */
    class DebugPort extends DebugBundle {
        val DoubleAllocation                = Output(Vec(paramQueueCapacity, Bool()))
        val DanglingAXIWriteResponse        = Output(Vec(paramQueueCapacity, Bool()))
    }

    @DebugSignal
    val debug   = IO(new DebugPort)

    /*
    * @assertion DoubleAllocation
    *   One slot in Transaction Queue must only be allocated once util next free.
    */
    (0 until paramQueueCapacity).foreach(i => {
        debug.DoubleAllocation(i) := io.allocate.en && io.allocate.strb(i) && regQueue(i).valid
        assert(!debug.DoubleAllocation(i),

    })

    /*
    * @assertion DanglingAXIWriteResponse
    *   Received Write Response on AXI B channel with no corresponding transaction.
    */
    (0 until paramQueueCapacity).foreach(i => {
        debug.DanglingAXIWriteResponse(i) := io.downstreamB.opDone.strb(i) &&
            (!regQueue(i).valid || !regQueue(i).bits.op.axi.WriteResponse.valid)
        assert(!debug.DanglingAXIWriteResponse(i),
            s"received BRESP for non-exist transaction at [${i}]")
    })
}


package cc.xiangshan.openncb.logical

import chisel3._
import chisel3.util.UIntToOH
import chisel3.experimental.BundleLiterals._
import org.chipsalliance.cde.config.Parameters
import org.chipsalliance.cde.config.Field
import cc.xiangshan.openncb.WithNCBParameters
import cc.xiangshan.openncb.chi.WithCHIParameters
import cc.xiangshan.openncb.chi.opcode.CHISNFOpcodesDAT
import cc.xiangshan.openncb.chi.channel.CHIChannelRXDAT
import cc.xiangshan.openncb.chi.field.CHIFieldSize
import cc.xiangshan.openncb.chi.CHIConstants
import cc.xiangshan.openncb.debug.CompanionConnection
import cc.xiangshan.openncb.debug.DebugBundle
import cc.xiangshan.openncb.debug.DebugSignal
import cc.xiangshan.openncb.logical.chi.CHILinkActiveBundle
import cc.xiangshan.openncb.logical.chi.CHILinkActiveManagerRX
import cc.xiangshan.openncb.logical.chi.CHILinkCreditManagerRX
import cc.xiangshan.openncb.util.ValidMux

/*
* NCB Upstream Port RXDAT
*/
object NCBUpstreamRXDAT {

    case class PublicParameters (
    )

    case object PublicParametersKey extends Field[PublicParameters]

    // companion connections
    @CompanionConnection
    def apply(uLinkActiveManager    : CHILinkActiveManagerRX,
              uTransactionQueue     : NCBTransactionQueue,
              uTransactionPayload   : NCBTransactionPayload)
             (implicit p: Parameters) = {
        val u   = Module(new NCBUpstreamRXDAT(uLinkActiveManager,
                                              uTransactionQueue,
                                              uTransactionPayload))

        // companion connection: LinkActiveManager
        u.io.linkState <> uLinkActiveManager.io.linkState

        // companion connection: NCBTransactionQueue
        u.io.queueUpstream <> uTransactionQueue.io.upstreamRxDat

        // companion connection: NCBTransactionPayload
        u.io.upstreamPayloadWrite <> uTransactionPayload.io.upstream.w

        u
    }
}

class NCBUpstreamRXDAT(val uLinkActiveManager       : CHILinkActiveManagerRX,
                       val uTransactionQueue        : NCBTransactionQueue,
                       val uTransactionPayload      : NCBTransactionPayload)
        (implicit val p: Parameters)
        extends Module with WithCHIParameters
                       with WithNCBParameters
                       with CHISNFOpcodesDAT {

    //
    def unsupportedCHIDataWidth(width: Int) =

    // public parameters
    val param   = p.lift(NCBUpstreamRXDAT.PublicParametersKey)
        .getOrElse(new NCBUpstreamRXDAT.PublicParameters)

    // local parameters
    protected def paramMaxBeatCount     = CHIConstants.CHI_MAX_PACKET_DATA_BITS_WIDTH / paramCHI.dataWidth

    /*
    * Module I/O
    */
    val io = IO(new Bundle {
        // upstream RX DAT port (CHI domain)
        val rxdat                   = CHIChannelRXDAT()

        // from LinkActiveManagerRX
        @CompanionConnection
        val linkState               = Flipped(chiselTypeOf(uLinkActiveManager.io.linkState))

        // upstream query from NCBTransactionQueue
        @CompanionConnection
        val queueUpstream           = Flipped(chiselTypeOf(uTransactionQueue.io.upstreamRxDat))

        // upstream write to NCBTransactionPayload
        @CompanionConnection
        val upstreamPayloadWrite    = Flipped(chiselTypeOf(uTransactionPayload.io.upstream.w))
    })

    // Upstream RXDAT Input Register
    protected val regRXDAT  = RegInit(init = {
        val resetValue = Wire(io.rxdat.undirectedChiselType)
        resetValue.lcrdv    := false.B
        resetValue.flitpend := false.B
        resetValue.flitv    := false.B
        resetValue.flit     := DontCare
        resetValue
    })

    io.rxdat.lcrdv      := regRXDAT.lcrdv
    regRXDAT.flitpend   := io.rxdat.flitpend
    regRXDAT.flitv      := io.rxdat.flitv

    when (io.rxdat.flitv) {
        regRXDAT.flit := io.rxdat.flit
    }

    // Module: Link Credit Manager
    protected val uLinkCredit   = Module(new CHILinkCreditManagerRX(
        paramMaxCount           = paramNCB.outstandingDepth,
        paramInitialCount       = paramNCB.outstandingDepth,
        paramCycleBeforeSend    = 0,
        paramEnableMonitor      = true
    ))

    regRXDAT.lcrdv              := uLinkCredit.io.lcrdv

    uLinkCredit.io.linkState    := io.linkState

    // credit loop back. no credit limit required on RXDAT channel.
    val uLinkCreditProvideBuffer = uLinkCredit.attachLinkCreditProvideBuffer()
    uLinkCreditProvideBuffer.io.linkCreditProvide   := regRXDAT.flitv

    // Module: CHI Opcode Decode
    protected val uDecoder = Module(new Decoder(Seq(
    //  ========================
        DataLCrdReturn,
    //  ------------------------
        WriteDataCancel,
    //  ------------------------
        NonCopyBackWrData
    //  ========================
    ), true))

    uDecoder.io.valid   := regRXDAT.flitv
    uDecoder.io.opcode  := regRXDAT.flit.Opcode.get

    protected val logicTransactionData      = uDecoder.is(NonCopyBackWrData)
    protected val logicTransactionCancel    = uDecoder.is(WriteDataCancel)
    protected val logicLCrdReturn           = uDecoder.is(DataLCrdReturn)

    protected val logicTxnIDToStrb          = VecInit(
        UIntToOH(regRXDAT.flit.TxnID.get, paramNCB.outstandingDepth).asBools)

    // link credit consume on flit valid
    uLinkCredit.io.monitorCreditConsume := regRXDAT.flitv

    // link credit return by DataLCrdReturn
    uLinkCredit.io.monitorCreditReturn  := logicLCrdReturn

    // transaction payload write
    io.upstreamPayloadWrite.en      := logicTransactionData
    io.upstreamPayloadWrite.strb    := logicTxnIDToStrb
    io.upstreamPayloadWrite.index   := {
        if (paramMaxBeatCount == 4) {
            VecInit(UIntToOH(regRXDAT.flit.DataID.get, 4).asBools)
        } else if (paramMaxBeatCount == 2) {
            VecInit(UIntToOH(regRXDAT.flit.DataID.get(1, 1), 2).asBools)
        } else if (paramMaxBeatCount == 1) {
            VecInit(true.B)
        } else {
            unsupportedCHIDataWidth(paramCHI.dataWidth)
        }
    }
    io.upstreamPayloadWrite.data    := regRXDAT.flit.Data.get
    io.upstreamPayloadWrite.mask    := regRXDAT.flit.BE.get

    // transaction queue update
    io.queueUpstream.cancel.en      := logicTransactionCancel
    io.queueUpstream.cancel.strb    := logicTxnIDToStrb

    io.queueUpstream.writeData.en   := logicTransactionData
    io.queueUpstream.writeData.strb := logicTxnIDToStrb

    // transaction queue query (for debug)
    io.queueUpstream.query.en       := regRXDAT.flitv
    io.queueUpstream.query.strb     := logicTxnIDToStrb

    // assertions & debugs
    class DebugPort extends DebugBundle {
        // flat
        val TxnIDNonExist               = Output(Bool())
        val TxnIDOutOfRange             = Output(Bool())
        val WriteCancelOnNonPtl         = Output(Bool())
        val WriteCancelNotSupported     = Output(Bool())
        val WriteFullWithParitalBE      = Output(Bool())

        // submodule
        val linkCredit                  = chiselTypeOf(uLinkCredit.debug)
        val linkCreditProvide           = chiselTypeOf(uLinkCreditProvideBuffer.debug)
        val decoder                     = chiselTypeOf(uDecoder.debug)
    }

    @DebugSignal
    val debug   = IO(new DebugPort)

    debug.linkCredit        <> uLinkCredit.debug
    debug.linkCreditProvide <> uLinkCreditProvideBuffer.debug
    debug.decoder           <> uDecoder.debug

    /*
    * @assertion TxnIDNonExist
    *   Received a flit with TxnID with-in outstanding depth range but did not exist.
    */
    debug.TxnIDNonExist := regRXDAT.flitv && !io.queueUpstream.query.result.valid
    assert(!debug.TxnIDNonExist,
        s"non-exist TxnID (no related transaction found)")

    /*
    * @assertion TxnIDOutOfRange
    *   Received a flit with TxnID out of outstanding depth range.
    */
    debug.TxnIDOutOfRange := regRXDAT.flitv && regRXDAT.flit.TxnID.get >= paramNCB.outstandingDepth.U
    assert(!debug.TxnIDOutOfRange,
        s"non-exist TxnID (out of outstanding depth)")

    /*
    * @assertion WriteCancelOnNonPtl
    *   'WriteDataCancel' reply was only allowed for 'Write*Ptl'.
    */
    debug.WriteCancelOnNonPtl := regRXDAT.flitv && logicTransactionCancel && !io.queueUpstream.query.result.WritePtl
    assert(!debug.WriteCancelOnNonPtl,
        s"WriteDataCancel only allowed for Write*Ptl")

    /*
    * @assertion WriteCancelNotSupported
    *   'WriteDataCancel' not supported when 'writeCancelable = false' configured.
    */
    debug.WriteCancelNotSupported := regRXDAT.flitv && logicTransactionCancel && !paramNCB.writeCancelable.B
    assert(!debug.WriteCancelNotSupported,
        s"WriteDataCancel not supported (writeCancelable = false)")

    /*
    * @assertion WriteFullWithParitalBE
    *   'BE' must be all asserted for 'Write*Full'.
    */
    debug.WriteFullWithParitalBE := regRXDAT.flitv && io.queueUpstream.query.result.WriteFull && !regRXDAT.flit.BE.get.andR
    assert(!debug.WriteFullWithParitalBE,
        s"partial BE on Write*Full")
}

throw new IllegalArgumentException(s"unsupported CHI data width: ${width}")


package cc.xiangshan.openncb.logical.chi

import chisel3._
import cc.xiangshan.openncb.chi.WithCHIParameters
import org.chipsalliance.cde.config.Parameters

abstract class AbstractCHILinkActiveManager extends Module


package cc.xiangshan.openncb.logical.chi

import chisel3._
import chisel3.util.log2Up
import chisel3.util.RegEnable
import chisel3.util.PopCount
import org.chipsalliance.cde.config.Parameters
import cc.xiangshan.openncb.chi.CHIConstants._
import cc.xiangshan.openncb.debug.DebugBundle
import cc.xiangshan.openncb.debug.DebugSignal

/*
* CHI Link Credit Manager for TX channels.
*
* * Supported Link-Layer states: STOP, ACT, RUN, DEACT.
*
* @param maxCount   Specify the maximum grantable link credit count. By default,
*                   {@see cc.xiangshan.openncb.chi.CHIConstants#CHI_MAX_REASONABLE_LINK_CREIDT_COUNT}
*/
class CHILinkCreditManagerTX(val paramMaxCount      : Int   = CHI_MAX_REASONABLE_LINK_CREDIT_COUNT)
        extends AbstractCHILinkCreditManager {

    // local parameters
    protected def paramLinkCreditCounterWidth: Int = log2Up(paramMaxCount + 1)

    // variable checks
    require(paramMaxCount > 0, s"maxCount > 0: maxCount = ${paramMaxCount}")

    require(paramMaxCount <= CHI_MAX_REASONABLE_LINK_CREDIT_COUNT,

    /*
    * Module I/O:
    *
    * @io input     linkState               : Link-layer State,
    *                                         directly comes from LinkActiveManager in general.
    *
    * @io output    linkCreditCount         : Granted Link Credit Count, width depends on 'maxCount'.
    * @io output    linkCreditAvailable     : Granted Link Credit Available.
    * @io input     linkCreditConsume       : Link Credit Consume, consume one granted link credit
    *                                         when asserted for sending a normal flit.
    * @io input     linkCreditReturn        : Link Credit Return, return one granted link credit
    *                                         when asserted for sending a credit return flit.
    *
    * @io input     lcrdv                   : Link Credit Valid, connects to TX*LCRDV.
    */
    val io = IO(new Bundle {
        // implementation local link-layer state signals
        val linkState               = Input(CHILinkActiveBundle())

        // implementation local credit signals
        val linkCreditCount         = Output(UInt(paramLinkCreditCounterWidth.W))
        val linkCreditAvailable     = Output(Bool())

        val linkCreditConsume       = Input(Bool())
        val linkCreditReturn        = Input(Bool())

        // CHI link-layer signals
        val lcrdv                   = Input(Bool())
    })

    // input register of Link Credit Valid
    protected val regLcrdv  = RegNext(next = io.lcrdv, init = false.B)

    // Link Credit counter
    protected val regLinkCreditCounter  = RegInit(init = 0.U(paramLinkCreditCounterWidth.W))

    when (regLcrdv && !io.linkCreditConsume && !io.linkCreditReturn) {
        regLinkCreditCounter := regLinkCreditCounter + 1.U
    }.elsewhen (!regLcrdv && (io.linkCreditConsume || io.linkCreditReturn)) {
        regLinkCreditCounter := regLinkCreditCounter - 1.U
    }

    // module output
    io.linkCreditCount      := regLinkCreditCounter
    io.linkCreditAvailable  := regLinkCreditCounter =/= 0.U

    // assertions & debugs
    /*
    * Port I/O: Debug
    */
    class DebugPort extends DebugBundle {
        val LinkActiveStateNotOneHot            = Output(Bool())
        val LinkCreditConsumeOutOfRun           = Output(Bool())
        val LinkCreditReturnOutOfDeactivate     = Output(Bool())
        val LinkCreditValidWhenLinkStop         = Output(Bool())
        val LinkCreditOverflow                  = Output(Bool())
        val LinkCreditUnderflow                 = Output(Bool())
    }

    @DebugSignal
    val debug   = IO(new DebugPort)

    /*
    * @assertion LinkActiveStateNotOneHot
    *   The states from Link Active must be one-hot.
    */
    private val debugLogicLinkactivePopcnt = PopCount(Seq(
        io.linkState.stop,
        io.linkState.activate,
        io.linkState.run,
        io.linkState.deactivate
    ))
    debug.LinkActiveStateNotOneHot := debugLogicLinkactivePopcnt =/= 1.U
    assert(!debug.LinkActiveStateNotOneHot,
        "linkactive state must be one-hot")

    /*
    * @assertion LinkCreditConsumeOutOfRun
    *   The consuming of a Link Credit must only occur in RUN state.
    */
    debug.LinkCreditConsumeOutOfRun := !io.linkState.run && io.linkCreditConsume
    assert(!debug.LinkCreditConsumeOutOfRun,
        "link credit consume out of RUN state")

    /*
    * @assertion LinkCreditReturnOutOfDeactivate
    *   The returning of a Link Credit must only occur in DEACTIVATE state.
    */
    debug.LinkCreditReturnOutOfDeactivate := !io.linkState.deactivate && io.linkCreditReturn
    assert(!debug.LinkCreditReturnOutOfDeactivate,
        "link credit return out of DEACTIVATE state")

    /*
    * @assertion LinkCreditValidWhenLinkStop
    *   The 'lcrdv' was not allowed to be asserted before Link ACTIVATE (In STOP state).
    */
    debug.LinkCreditValidWhenLinkStop := io.linkState.stop && io.lcrdv
    assert(!debug.LinkCreditValidWhenLinkStop,
        "unexpected 'lcrdv' during link STOP")

    /*
    * @assertion LinkCreditOverflow
    *   The 'lcrdv' was not allowed to be asserted when the Link Credit received exceeded
    *   the maximum number.
    */
    debug.LinkCreditOverflow := regLinkCreditCounter === paramMaxCount.U && regLcrdv
    assert(!debug.LinkCreditOverflow,
        s"link credit overflow (with maximum ${paramMaxCount})")

    /*
    * @assertion LinkCreditUnderflow
    *   The 'linkCreditConsume' was not allowed to be asserted when there was no
    *   Link Credit available in the current cycle.
    */
    debug.LinkCreditUnderflow := regLinkCreditCounter === 0.U && io.linkCreditConsume
    assert(!debug.LinkCreditUnderflow,
        "link credit underflow")
}

s"max maximum link credit count is ${CHI_MAX_REASONABLE_LINK_CREDIT_COUNT}, but ${paramMaxCount} configured")


package cc.xiangshan.openncb.util

import chisel3._

/*
* Standardized Addressable Read Write Port (1 Write, 1 Read)
*/

class AddressableReadWritePort(val addressWidth: Int, val dataWidth: Int) extends Bundle {

    // Write Port
    val w   = new AddressableWritePort(addressWidth, dataWidth)


package cc.xiangshan.openncb.util

import chisel3._

/*
* Standardized Addressable Write Port
*/
class AddressableWritePort(val addressWidth: Int, val dataWidth: Int) extends Bundle {

    // Write Port
    val en      = Input(Bool())
    val addr    = Input(UInt(addressWidth.W))
    val data    = Input(UInt(dataWidth.W))


package cc.xiangshan.openncb

import chisel3._
import chisel3.experimental.VecLiterals._
import chisel3.util.IrrevocableIO
import chisel3.util.Irrevocable
import chisel3.util.DecoupledIO
import chisel3.util.Decoupled

package object util {

    implicit class fromBooleanToVecLiteral(boolean: Boolean) {

        /*
        * Boolean to Bool Vec conversion, recommended style for constants.
        *
        * @param n Width of Bool Vec
        */

        /*
        * Boolean to Bool Vec Literal conversion, recommended style for constants.
        *
        * * Typically used for Vec.Lit
        *
        * @param n Width of Bool Vec Literal
        */
        def BVecLit(n: Int): Vec[Bool] =
            Vec(n, Bool()).Lit(Seq.fill(n)(boolean.B).zipWithIndex.map(l => l.swap) : _*)
    }

    implicit class sliceBitExtractionForUInt(data: UInt) {

        /*
        * Create a sub-slice bit extraction of the UInt by 'offset' and 'width'.
        *
        * @param offset Offset of UInt slice extraction
        * @param width  Width of UInt slice extraction
        */
        def extract(offset: Int, width: Int): UInt =
            data((offset + 1) * width - 1, offset * width)
    }

    implicit class irrevocableIOMapFunction[+T <: Data](gen: IrrevocableIO[T]) {

        /** Applies the supplied functor to the bits of this interface, returning a new
        * typed IrrevocableIO interface, with direciton of:
        *   B = A.mapTo(B) :
        *      B.bits  := A.bits
        *      B.valid := A.valid
        *      A.ready := B.ready
        *
        * @param f The function to apply to this IrrevocableIO's 'bits' with return type B
        * @return a new IrrevocableIO of type B
        */
        def mapTo[B <: Data](f: T => B): IrrevocableIO[B] = {
            val _map_bits = f(gen.bits)
            val _map = Wire(Irrevocable(chiselTypeOf(_map_bits)))
            _map.bits   := _map_bits
            _map.valid  := gen.valid
            gen.ready   := _map.ready
             _map
        }

        /** Applies the supplied functor to the bits of this interface, returning a new
        * typed IrrevocableIO interface, with direciton of:
        *   B = A.mapFrom(B) :
        *      A.bits  := B.bits
        *      A.valid := B.valid
        *      B.ready := A.ready
        *
        * @param f The function to apply to this IrrevocableIO's 'bits' with return type B
        * @return a new IrrevocableIO of type B
        */
        def mapFrom[B <: Data](f: T => B): IrrevocableIO[B] = {
            val _map_bits = f(gen.bits)
            val _map = Wire(Irrevocable(chiselTypeOf(_map_bits)))
            _map.bits   := _map_bits
            gen.valid   := _map.valid
            _map.ready  := gen.ready
             _map
        }
    }

    implicit class decoupledIOMapFunction[+T <: Data](gen: DecoupledIO[T]) {

        /** Applies the supplied functor to the bits of this interface, returning a new
        * typed DecoupledIO interface, with direciton of:
        *   A.mapTo(B) :
        *      B.bits  := A.bits
        *      B.valid := A.valid
        *      A.ready := B.ready
        *
        * @param f The function to apply to this DecoupledIO's 'bits' with return type B
        * @return a new DecoupledIO of type B
        */
        def mapTo[B <: Data](f: T => B): DecoupledIO[B] = {
            val _map_bits = f(gen.bits)
            val _map = Wire(Decoupled(chiselTypeOf(_map_bits)))
            _map.bits   := _map_bits
            _map.valid  := gen.valid
            gen.ready   := _map.ready
             _map
        }

        /** Applies the supplied functor to the bits of this interface, returning a new
        * typed DecoupledIO interface, with direciton of:
        *   A.mapFrom(B) :
        *      A.bits  := B.bits
        *      A.valid := B.valid
        *      B.ready := A.ready
        *
        * @param f The function to apply to this DecoupledIO's 'bits' with return type B
        * @return a new DecoupledIO of type B
        */
        def mapFrom[B <: Data](f: T => B): DecoupledIO[B] = {
            val _map_bits = f(gen.bits)
            val _map = Wire(Decoupled(chiselTypeOf(_map_bits)))
            _map.bits   := _map_bits
            gen.valid   := _map.valid
            _map.ready  := gen.ready
             _map
        }
    }
}

def BVec(n: Int): Vec[Bool] = Vec(n, boolean.B)