package sv2chisel.helpers.tools

import chisel3.experimental.{RunFirrtlTransform}

import firrtl._
import firrtl.ir._
import firrtl.annotations._
import firrtl.options.Dependency

import logger.LazyLogging

import scala.collection.mutable.{ArrayBuffer, HashSet}

case class ModulePresetAnnotation(target: ModuleTarget) extends SingleTargetAnnotation[ModuleTarget] {
  override def duplicate(n: ModuleTarget): ModulePresetAnnotation = this.copy(target = n)
}

case class ModulePresetChiselAnnotation(target: ModuleTarget) extends RunFirrtlTransform {
  def transformClass = classOf[ModulePreset]          // scalastyle:off public.methods.have.type
  def toFirrtl       = ModulePresetAnnotation(target) // scalastyle:off public.methods.have.type
}

/** Convert Top Reset Type of Annotated Modules For each module with expected annotation `ModulePresetAnnotation`
 *    - convert reset IO of any Type into AsyncResetType
 *    - add `PresetAnnotation` on this IO
 *
 *  Caveats:
 *    - Expected result is seen only at the emission : no more reset signal
 *    - Firrtl emission will only reflect modification of the Reset into AsyncResetType
 */
class ModulePreset extends Transform with DependencyAPIMigration with LazyLogging {

  override val prerequisites                    = firrtl.stage.Forms.HighForm
  override val optionalPrerequisiteOf           = Seq(Dependency[ModuleRename])
  override def invalidates(a: firrtl.Transform) = false

  /** Recursively update all annotated modules
   *    - annotated modules are removed from the moduleSet
   *    - instance declared inside an annotated modules are added to the moduleSet
   *    - run again until moduleSet is empty
   *
   *  @param circuit
   *    the circuit
   *  @param annotations
   *    all the annotations
   *  @return
   *    updated annotations
   */
  def update(
      cs: CircuitState,
      presetModules: Set[ModuleTarget],
      topPresetModules: Set[ModuleTarget]
  ): CircuitState = {

    // Annotations to be appended and returned as result of the transform
    val annos         = ArrayBuffer(cs.annotations.toSeq:_*)
    val moduleSet     = HashSet(presetModules.toSeq:_*)
    val circuitTarget = CircuitTarget(cs.circuit.main)

    /** Update annotated module
     *    - convert reset into AsyncResetType
     *    - add preset annotation for top level port only (useless to add for all)
     */
    def processModule(m: DefModule): DefModule = {
      val moduleTarget = circuitTarget.module(m.name)

      def processPorts(port: Port): Port = {
        if (port.name == "reset") {
          logger.debug(s"[debug] Update reset of ${m.name}")
          val target = moduleTarget.ref(port.name)
          if (topPresetModules.contains(moduleTarget)) annos += PresetAnnotation(target)
          port.copy(tpe = AsyncResetType)
        } else {
          port
        }
      }

      def processStatements(stmt: Statement): Statement = {
        stmt match {
          case i: WDefInstance =>
            logger.debug(

            )
            moduleSet += circuitTarget.module(i.module)
            i
          case _ => stmt.mapStmt(processStatements)
        }
      }

      if (moduleSet.contains(moduleTarget)) {
        moduleSet -= moduleTarget
        m.mapPort(processPorts).mapStmt(processStatements)
      } else {
        m
      }
    }
    // ensure that modules are processed in hierarchy order (avoid multiple runs)
    val modules = cs.circuit.modules.reverse.map(processModule)
    val circuit = cs.circuit.copy(modules = modules.reverse)
    val result  = cs.copy(circuit = circuit, annotations = annos.toSeq)
    if (moduleSet.isEmpty) {
      result
    } else if(moduleSet.toSet == presetModules) {
      logger.error(s"[fatal] Aborting module preset update! ${presetModules.map(_.prettyPrint()).mkString(",")}")
      logger.error(s"[fatal] Expect further errors if the top-level reset cannot be found.")
      logger.error(s"[fatal] Did you specify the right ModuleTarget?")
      result
    } else {
      logger.warn("[info] Re-running ModulePreset Propagation")
      update(result, moduleSet.toSet, topPresetModules)
    }
  }

  def execute(state: CircuitState): CircuitState = {
    // Collect all user-defined PresetAnnotation
    val presets = state.annotations
      .collect { case m: ModulePresetAnnotation => m }
      .groupBy(_.target)
      .keySet
    // No ModulePresetAnnotation => no need to walk the IR
    if (presets.size == 0) {
      state
    } else {
      update(state, presets, presets)
    }
  }
}

s"[debug] Registering instance ${i.name} of ${i.module} for AsyncReset Propagation"


// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2020 The sv2chisel Authors. All rights reserved.

package sv2chiselHelpersTests

import sv2chiselHelpersTests.utils._
import sv2chisel.helpers.bundleconvert._

import chisel3._
import chisel3.stage.ChiselStage

import org.scalatest._
import flatspec._

class bundleconvertSpec extends AnyFlatSpec with VerilogMatchers {
  val setTestRunDir = Array("--target-dir", "test_run_dir")

  behavior of "BundleSubRangeAccess"

  it should "allow field extraction by range" in {
    class SubRangeBundle() extends RawModule {
      class MyBundle extends Bundle {
        val a = Bool() // index (7, 7)
        val b = Bool() // index (6, 6)
        val c = UInt(5.W) // index (5, 1)
        val d = Bool() // index (0,0)
      }

      out(0,0) := true.B
      out(5,1) := 4.U
      out(6,6) := true.B
      out(7,7) := false.B
    }
    val verilog = (new ChiselStage()).emitVerilog(new SubRangeBundle(), setTestRunDir)

    verilog should containStr ("assign out_a = 1'h0;")
    verilog should containStr ("assign out_b = 1'h1;")
    verilog should containStr ("assign out_c = 5'h4;")
    verilog should containStr ("assign out_d = 1'h1;")
  }

}


// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2020 The sv2chisel Authors. All rights reserved.

package sv2chisel
package ir

import logger.{InfoLogging}
import org.antlr.v4.runtime.{CommonTokenStream}

package object expressionToLiteral {
  implicit def expToLiteral(e: Expression) = new ExpressionToLiteral(e)
}

class ExpressionToLiteral(e: Expression) extends InfoLogging {
  var currentSourceFile : Option[SourceFile] = None
  var currentStream : Option[CommonTokenStream] = None
  implicit def svnode2Interval(n:SVNode): Interval = n.tokens

  val ui = UndefinedInterval

  def toLiteralOption(implicit currentSourceFile : Option[SourceFile], currentStream : Option[CommonTokenStream]): Option[Expression] = {
    this.currentSourceFile = currentSourceFile
    this.currentStream = currentStream
    e match {
      case n: Number =>
        n.getBigInt match {
          case Some(bg) if (bg < 0) =>
            n.base match {
              case NumberDecimal => Some(SIntLiteral(n.tokens, bg, n.width))
              case b =>
              warn(n, s"[fixable] Unable to convert negative numbers with base $b to Literal")
              None
            }

          case Some(bg) => Some(UIntLiteral(n.tokens, bg, n.width, n.base))

          case _ =>
            warn(n, s"Unable to convert ${n.serialize} to BigInt")
            None
        }

      case r: Reference =>
        r.tpe match {
          case i:IntType =>
            warn(r, s"Tech Debt: casting IntType to UIntType (TODO: proper signed management)")
            Some(DoCast(r.tokens, e, HwExpressionKind, UIntType(UndefinedInterval, UnknownWidth(), i.base)))
          case _ =>
            trace(r, s"Reference with tpe: ${r.tpe.serialize}")
            None
        }

      case _ =>
        trace(e, s"Cannot convert expression ${e.serialize} to Literal")
        None
    }


// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2020 The sv2chisel Authors. All rights reserved.

/*
 All this transform is to be redone
 - SourceFlow = RHS = some element being read, used / assign from
 - SinkFlow = LHS = some element being written / assign towards

 To be done in the following way

 NOTE: important, references shall be managed as in inferuints:
       with virtual references such as vec[_], matrix[_][_]

 NOTE: due to repeat, push warning into a Set, so they are all printed at once at
       the end of the the transform, without duplicate
       could use a "done" dictionary to lookup for and do not process/print msg for "done" refs

Repeat until updated map is empty
  - clear updated map
  - Keep for each reference R:
    - Seq[A: assignations (R as SinkFlow)]
    - Seq[B usages (R as SourceFlow)]
    - Seq[C Unknown: R as potential Sink OR Flow] (only assigns in IOs of DefInstance)
   => NOTE: Pass#1 this is already a process path because assignations can be applied in place, as for source usage
   => NOTE: no need to count again / modify for ref in "DONE"

  - Analyse results: (A, B, C) match
    - (Seq(), Seq(), Seq()) => warn unused reference
    - (Seq(), Seq(), Seq(e)) => warn cannot conclude anything but treat as if R is assigned by e
    - (Seq(), Seq(), s) => warn cannot conclude anything and do not update anything
    - (Seq(), s, Seq(e)) => e is the place where R should be referenced as Sink (assigned by submodule output)
    - (Seq(e), _, e:_) if assumeValidVerilog => foreach elt in e => R should be treated as source in elt
    - (Seq(e), _, e:_) => warn cannot conclude anything
    - (s, Seq(), Seq()) => warn declared & assigned but left unconnected
    - (Seq(), s, Seq()) => warn undeclared ref
    - (Seq(e), s, Seq()) => // nothing to do
    - (s, _, e:_) => if asssumeValidVerilog => warn multiple assigns but assume R as Source in each elt of e
    - (s, _, e:_) => debug nothing can be deduced for now for r flow in elmts of e

  - Apply update
    - this might be the tricky one => how to find the right usage ?
      - well it is actually dead simple: all Reference to R in updated map with Unknown FLow in DefInstance Assign are to be updated.

*/

package sv2chisel
package transforms

import sv2chisel.ir._
import sv2chisel.Utils._

import collection.mutable.{HashMap, HashSet, ArrayBuffer, LinkedHashMap}

class FlowReferences(
    val options: TranslationOptions,
    val assumeValidVerilog: Boolean = true) extends DescriptionBasedTransform {
  private val ui = UndefinedInterval

  class ModuleStore() {
    class ModulePorts(val name: String){
      val declaration = new LinkedHashMap[String, Assign]()
      val namedPorts = new LinkedHashMap[String, Assign]()
      val noNamePorts = new ArrayBuffer[Assign]()

      private def getPortsKind(p: Seq[Assign], expected: Option[Assign]): Assign = {
        (p, expected) match {
          case (Seq(), None) => throwInternalError("Unexpected empty port list")
          case (Seq(), Some(a)) => a
          case (s, None) => getPortsKind(s.tail, Some(s.head))
          case (s, Some(e)) =>
            val exp = (e, s.head) match {
              case (_: NamedAssign, _:NamedAssign) => e
              case (_: NoNameAssign, _:NoNameAssign) => e
              case _ => throwInternalError(s"Incompatible parameter list: got both $e and ${s.head} in same list.")
            }
            getPortsKind(s.tail, Some(exp))
        }
      }

      private def getSeqFlows(s: Seq[Assign], ports: Seq[Assign]): Seq[Flow] = {
        val missingEntries = ports.size - s.size
        val ref = s ++ Seq.fill(missingEntries)(NoNameAssign(ui, UndefinedExpression(), UnknownFlow))

        ref.zip(ports).map(t => {
          (t._1.flow, t._2.flow) match {
            case (UnknownFlow, f2) => f2
            case (f1, UnknownFlow) => f1
            case (f1, f2) if (f1 == f2) => f1
            case (f1, f2) if (f1 != f2) =>
              critical(t._2, s"Unexpected direction for port ${t._2} where ${t._1} was expected. (Expression are given for context, only mismatching Flows are causing this message)")
              f1
          }
        })
      }

      private def getNamedFlows(s: LinkedHashMap[String, Assign], ports: Seq[Assign]): Seq[Flow] = {
        ports.map(p => {
          p match {
            case na: NamedAssign =>
              s.get(na.name) match {
                case Some(a) => a.flow
                case None => UnknownFlow
              }
            case _ => throwInternalError("Should not be here")
          }
        })
      }

      def declare(ports: Seq[Port]): Unit = {
        ports match {
          case Seq() => info(s"Declaring actual port directions for module $name")
          case s => info(s.head, s"Declaring actual port directions for module $name")
        }

        ports.foreach(p => {
          val flow = p.direction match {
            case _:Input => SourceFlow // NOTE: the connected expr shall be a source flow
            case _:Output => SinkFlow
            case _ => UnknownFlow
          }
          declaration += ((p.name, NamedAssign(ui, p.name, UndefinedExpression(), flow)))
        })
      }

      def registerInference(ports: Seq[Assign]) : Unit = {
        // do not risk error while the result won't be used anyway
        if(declaration.isEmpty){
          getPortsKind(ports, None) // just ensure they are all the same type
          ports.zipWithIndex.map(t => {
            t._1 match {
              case na: NamedAssign =>
                (namedPorts.get(na.name), na.flow) match {
                  // nothing to update
                  case (_, UnknownFlow) =>
                  case (Some(a), f) if(a.flow == f) =>

                  // DO UPDATE
                  case (Some(NamedAssign(_, _, _, UnknownFlow, _ ,_, _)), _) => namedPorts(na.name) = na
                  case (None, _) => namedPorts += ((na.name, na))

                  // ERROR on defined flow override
                  case (Some(a), f) if(a.flow != f) =>
                    critical(na, s"Ignored attempt to update direction for port ${na.name} from ${a.flow} to $f.")
                }

              case na: NoNameAssign =>
                (noNamePorts(t._2), na.flow) match {
                  // nothing to update
                  case (_, UnknownFlow) =>
                  case (a, f) if(a.flow == f) =>

                  // DO UPDATE
                  case (NoNameAssign(_, _, UnknownFlow, _, _, _, _), _) =>
                    if(t._2 < noNamePorts.size) {
                      noNamePorts(t._2) = na
                    } else {
                      noNamePorts += na
                    }

                  // ERROR on defined flow override
                  case (a, f) if(a.flow != f) =>
                    critical(na, s"Ignored attempt to update direction for port ${t._2} from ${a.flow} to $f.")
                }
              case _ => // do nothing
            }
          })
        }
      }

      def getExpectedFlows(ports: Seq[Assign]): Seq[Flow] = {
        //first look for actual module declaration
        val kind = getPortsKind(ports, None)
        (declaration.isEmpty, namedPorts.isEmpty, noNamePorts.isEmpty, kind) match {
          case (false, _, _, _: NamedAssign) => getNamedFlows(declaration, ports)
          case (false, _, _, _: NoNameAssign) => getSeqFlows(declaration.values.toSeq, ports)
          case (_, false, _, _: NamedAssign) => getNamedFlows(namedPorts, ports)
          case (_, false, _, _: NoNameAssign) => getSeqFlows(namedPorts.values.toSeq, ports)
          case (_, _, false, _: NamedAssign) => ports.map(_ => UnknownFlow)
          case (_, _, false, _: NoNameAssign) => getSeqFlows(noNamePorts.toSeq, ports)
          case _ => ports.map(_ => UnknownFlow)
        }
      }
    }

    private val store = new HashMap[String, ModulePorts]

    def register(m: DefModule): Unit = {
      store.get(m.name) match {
        case Some(m@_) => throwInternalError(s"Unexpected duplicated declaration for module ${m.name}")
        case None =>
          val mp = new ModulePorts(m.name)
          mp.declare(m.ports)
          store += ((m.name, mp))
      }
    }

    def registerInference(name: String, ports: Seq[Assign]) : Unit = {
      store.get(name) match {
        case Some(mp) => mp.registerInference(ports)
        case None =>
          val mp = new ModulePorts(name)
          mp.registerInference(ports)
          store += ((name, mp))
      }
    }

    def getExpectedFlows(name: String, ports: Seq[Assign]): Seq[Flow] = { // Assign or flow ?
      store.get(name) match {
        case Some(mp) => mp.getExpectedFlows(ports)
        case None => ports.map(_ => UnknownFlow)
      }
    }
  }
  val moduleStore = new ModuleStore()

  override val preprocessDescription = Some(((d: Description) => {
    d match {
      case m: DefModule => moduleStore.register(m); m
      case d => d
    }
  }))

  def processDescription(d: Description): Description = {
    d match {
      case m: Module => processModule(m)
      case d => d
    }
  }

  def processModule(m: DefModule): DefModule = {
    val extAssigned = new HashMap[String, Boolean]()
    val inAssigned = new HashSet[String]()
    val implicitSources = new HashSet[String]()
    val wasInferredSink = new HashSet[String]()
    var newImplicitSources = false

    // Note: only useful for lhs refs (no doPrim operations except ignored concat)
    def getRef(e: Expression): Option[Reference] = {
      e match {
        case r: Reference => Some(r)
        case s: SubField => getRef(s.expr) // assuming all subfield with same dir
        case s: SubIndex => getRef(s.expr)
        case s: SubRange => getRef(s.expr)
        case _: Concat => None
        // most of it
        case _ => None
      }
    }

    // first pass => hashmap with assignation to references
    def visitConnect(c: Connect): Unit = {
      getRef(c.loc) match {
        case None =>
        case Some(r) =>
          val ref = r.serialize

          extAssigned.get(ref) match {
            case Some(r@_) => // nothing to do
            // registering as inAssigned => not as strong as extAssigned
            case None => inAssigned += ref
          }
      }
    }

    def visitDefParam(l: DefParam): Unit = {
      if (extAssigned.contains(l.name)) {
        warn(l, s"Multiple definitions of ${l.name}")
      } else {
        extAssigned += ((l.name, true))
      }
    }

    def visitStatement(s: Statement): Unit = {
      s match {
        case c: DefParam => visitDefParam(c)
        case c: Connect => visitConnect(c)
        case p: Port => visitPort(p)
        case _ => s.foreachStmt(visitStatement)
      }
    }

    def visitPort(p: Port): Unit = {
      if (extAssigned.contains(p.name)) {
        warn(p, s"Multiple definitions of ${p.name}")
      } else {
        p.direction match {
          case _: Output => extAssigned += ((p.name, false))
          case _ => extAssigned += ((p.name, true))
        }
      }
    }

    // TO DO : we are missing includes here ...
    m.foreachParam(visitDefParam)
    m.foreachStmt(visitStatement)

    def getFlow(r: Reference, expected: Option[Flow]): Flow = {
      val ref = r.serialize
      val inAssign = inAssigned.contains(ref)
      val imply = implicitSources.contains(ref)
      val sink = wasInferredSink.contains(ref) // assume file ordering

      (extAssigned.get(ref), inAssign, imply, sink, expected) match {
        case (None, false, false, false, None) => UnknownFlow

        // back-propagated sink/source over previously UnknownFlow
        case (None, false, false, false, Some(SinkFlow)) => wasInferredSink += ref; SinkFlow

        // reuse of elsewhere used as sink signal
        case (None, false, _, true, None) => SourceFlow

        // submodule output
        case (None, false, true, false,  None) =>
          wasInferredSink += ref
          SinkFlow

        case (None, true, true, _, None) => throwInternalError("Impossible by design")
        case (None, true, false, _, None) => SourceFlow
        // known inputs & params
        case (Some(true), _, _, _, Some(SinkFlow)) => SinkFlow // basically all connects
        case (Some(true), _, _, _, _) => SourceFlow

        // known outputs
        case (Some(false), _, _, _, Some(f)) => f // outputs signal used in computations
        case (Some(false), _, _, _, _) => SinkFlow

        // implicit source (from doprim, call or cast)
        case (None, false, false, _, Some(SourceFlow)) if (assumeValidVerilog) =>
          debug(r, s"Adding implicit source $ref")
          implicitSources += ((ref))
          newImplicitSources = true
          SourceFlow

        case (None, _, _, _, Some(f)) => f
      }
    }
    def reduceFlow(f1: Flow, f2: Flow): Flow = {
      (f1, f2) match {
        case (SourceFlow, SourceFlow) => SourceFlow
        case (SinkFlow, SinkFlow) => SinkFlow
        case (SinkFlow, UnknownFlow) if(assumeValidVerilog) => SinkFlow
        case (UnknownFlow, SinkFlow) if(assumeValidVerilog) => SinkFlow
        case (SourceFlow, UnknownFlow) if(assumeValidVerilog) => SourceFlow
        case (UnknownFlow, SourceFlow) if(assumeValidVerilog) => SourceFlow
        case _ => UnknownFlow
      }
    }

    def backPropagateFlow(e: Expression, f: Flow): Expression = {
      e match {
        case r: Reference =>
          (r.flow, f) match {

          r.copy(flow = f)
        case s: SubField =>
          val expr = backPropagateFlow(s.expr, f)
          s.copy(expr = expr, flow = expr.flow)

        case s: SubIndex =>
          val expr = backPropagateFlow(s.expr, f)
          s.copy(expr = expr, flow = expr.flow)

        case s: SubRange =>
          val expr = backPropagateFlow(s.expr, f)
          s.copy(expr = expr, flow = expr.flow)

        case _ => e
      }
    }

    // SECOND PASS => apply flow to expressions
    var instCount = 0
    def processExpression(e: Expression, expected: Option[Flow]): Expression = {
      e match {
        case u: UndefinedExpression =>
          // important for Port Emission (connecting DontCare to Undefined Inputs)
          expected match {
            case Some(f) => u.copy(flow = f)
            case _ => u
          }

        case r: Reference => r.copy(flow = getFlow(r, expected))
        case s: SubField =>
          // flows should be applied only to bundle fields not whole bundle
          // additionnaly direction should be involved ? (not sure it is useful for verilog)
          // Note that for verilog this should be fine => in any field of a struct is assigned it means the whole strcut should be assigned in the same place
          // Edge case are definitely not covered though
          // struct defined within module ; 1 field assigned from input port another assigned from submodule output
          val expr = processExpression(s.expr, expected)
          s.copy(expr = expr, flow = expr.flow)

        case s: SubIndex =>
          // above notice concerning bundle somehow apply also here ...
          // definitely need to introduce some kind of reference target management
          val index = processExpression(s.index, Some(SourceFlow))
          val expr = processExpression(s.expr, expected)
          s.copy(expr = expr, index = index, flow = expr.flow)

        case s: SubRange =>
          // above notice concerning bundle somehow apply also here ...
          // definitely need to introduce some kind of reference target management
          val expr = processExpression(s.expr, expected)
          val left = processExpression(s.left, Some(SourceFlow))
          val right = processExpression(s.right, Some(SourceFlow))
          s.copy(expr = expr, left = left, right = right, flow = expr.flow)

        case d: DoPrim => // all ops work the same
          // in valid verilog all operand of a primary operation are assumed to be SourceFlow
          // assuming valid verilog: use this information to back propagate info to reference
          val ar = d.args.map(processExpression(_, Some(SourceFlow)))
          ar.map(_.flow).reduce(reduceFlow) match {
            case SourceFlow => // fine
            case SinkFlow => critical(d, s"Ignored SinkFlow infered for primary op : ${d.serialize}")
            case _ => warn(d, s"Unable to infer flow for ${d.serialize}. Note: Primary operator are always SourceFlow.")
          }
          d.copy(args= ar)

        case c: Concat =>
          val ar = c.args.map(processExpression(_, expected))
          val flow = ar.map(_.flow).reduce(reduceFlow)
          // back propagate over UnknownFlow if any
          val backPropagatedArgs = (expected, flow) match {
            case (None, UnknownFlow) => ar
            case (None, SinkFlow) => ar.map(backPropagateFlow(_, SinkFlow))
            case _ => ar
          }
          c.copy(args= backPropagatedArgs, flow = flow)

        case a: NamedAssign =>
          val expr = processExpression(a.expr, expected)
          a.copy(expr = expr, flow = expr.flow)

        case a: NoNameAssign =>
          val expr = processExpression(a.expr, expected)
          a.copy(expr = expr, flow = expr.flow)

        case d: DoCast =>
          d.copy(expr = processExpression(d.expr, Some(SourceFlow)))

        case c: ComplexValues =>
          c.mapExpr(e => processExpression(e, Some(SourceFlow)))

        case _ => trace(e, s"ignored flow propagation for ${e.serialize}"); e
      }
    }

    def processAssign(a: Assign, expected: Option[Flow]): Assign = {
      a match {
        case _: AutoAssign => a
        case na: NamedAssign =>
          val expr = processExpression(na.expr, expected)
          na.copy(expr = expr, flow = expr.flow)
        case na: NoNameAssign =>
          val expr = processExpression(na.expr, expected)
          na.copy(expr = expr, flow = expr.flow)
      }
    }

    def processInstance(d: DefInstance): DefInstance = {
      instCount +=1
      val params = d.paramMap.map(processAssign(_, Some(SourceFlow)))
      // the whole trick to auto infer ports directions lies here
      // we need not to erase what was decided previously
      val exp = moduleStore.getExpectedFlows(d.module.serialize, d.portMap)
      val ports = d.portMap.zip(exp).map(t => {
        (t._1.flow, t._2) match {
          case (UnknownFlow, UnknownFlow) => processAssign(t._1, None)
          case (UnknownFlow, f) => processAssign(t._1, Some(f))
          case _ => t._1
        }
      })
      moduleStore.registerInference(d.module.serialize, ports)
      d.copy(paramMap = params, portMap = ports)
    }

    def processStatement(s: Statement): Statement = {
      s match {
        case c: Connect =>
          val loc = processExpression(c.loc, Some(SinkFlow))
          val expr = processExpression(c.expr, Some(SourceFlow))
          c.copy(loc = loc, expr = expr)

        case d: DefInstance => processInstance(d)

        case _ => s.mapExpr(processExpression(_, Some(SourceFlow))).mapStmt(processStatement)
      }
    }

    var result: DefModule = m.mapStmt(processStatement)
    var maxIter = 3
    while((newImplicitSources || !wasInferredSink.isEmpty) && maxIter > 0){
      info(m, s"Running FlowReference Transform another time on module ${m.name}")
      newImplicitSources = false
      instCount = 0
      wasInferredSink.foreach(ref => extAssigned += ((ref, true)))
      wasInferredSink.clear()
      maxIter -= 1
      result = result.mapStmt(processStatement)
    }
    if (newImplicitSources || !wasInferredSink.isEmpty) {
      critical(m, "Unable to completely achieve FlowReference Transform within the 4 successives runs.")
    }

    def debugPrintInstances(s:Statement): Unit = {
      s match {
        case d: DefInstance => debug(d, d.serialize)
        case _ => s.foreachStmt(debugPrintInstances)
      }
    }
    if(instCount > 0){
      debug(s"### Detail of instances in module ${result.name}: ")
      result.foreachStmt(debugPrintInstances)
    } else {
      debug(s"### No Instances in module ${result.name}.")
    }
    result
  }
}

            case (UnknownFlow, SinkFlow) =>
              wasInferredSink += (r.serialize)
              debug(e, s" > Recording that ${r.serialize} was used as sink")


// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2020 The sv2chisel Authors. All rights reserved.

package sv2chiselTests

import sv2chiselTests.utils._
import logger._
import sv2chisel.{TranslationOptions, ChiselizerOptions}

class BlackBoxSpec extends Sv2ChiselSpec {
  Logger.setLevel(LogLevel.Warn)

  behavior of "BlackBox"

  it should "work with inline ports with resource" in {

    val bb = wrapInModule("""
        |input  [7:0] i_a;
        |input  [7:0] i_b;
        |input        i_c;
        |output [7:0] o;
        |output [7:0] o_v;
        |
        |assign o = sum(i_a, i_b);
        |assign o_v = sum(i_a, i_c);
        """.stripMargin,
        "my_black_box"
      )

    val main = wrapInModule("""
        |input  [7:0] i_a;
        |input  [7:0] i_b;
        |input        i_c;
        |output [7:0] o;
        |output [7:0] o_v;
        |
        |my_black_box inst(
        |  .i_a(i_a),
        |  .i_b(i_b),
        |  .i_c(i_c),
        |  .o(o),
        |  .o_v(o_v)
        |);
        """.stripMargin
      )
    val options = TranslationOptions().copy(
      chiselizer = ChiselizerOptions().copy(baseBlackboxRessourcePath = Some("./test_run_dir/resources/"))
    )
    val result = emit(bb, main, Some("src/main/resources/project/hdl/my_module.sv"), options)

    result should containStr ( "import chisel3.util.HasBlackBoxResource")
    result should containStr ( "class my_black_box() extends BlackBox with HasBlackBoxResource {")
    result should containStr (
      "val io = IO(new Bundle {",
        "val i_a = Input(UInt(8.W))",
        "val i_b = Input(UInt(8.W))",
        "val i_c = Input(Bool())",
        "val o = Output(UInt(8.W))",
        "val o_v = Output(UInt(8.W))",
      "})"
    )

    result should containStr (
      "val inst = Module(new my_black_box)",
      "inst.io.i_a := i_a",
      "inst.io.i_b := i_b",
      "inst.io.i_c := i_c",
      "o := inst.io.o",
      "o_v := inst.io.o_v"
    )
  }

  it should "work with inline ports without ressource" in {

    val bb = wrapInModule("""
        |input  [7:0] i_a;
        |input  [7:0] i_b;
        |input        i_c;
        |output [7:0] o;
        |output [7:0] o_v;
        |
        |assign o = sum(i_a, i_b);
        |assign o_v = sum(i_a, i_c);
        """.stripMargin,
        "my_black_box"
      )

    val main = wrapInModule("""
        |input  [7:0] i_a;
        |input  [7:0] i_b;
        |input        i_c;
        |output [7:0] o;
        |output [7:0] o_v;
        |
        |my_black_box inst(
        |  .i_a(i_a),
        |  .i_b(i_b),
        |  .i_c(i_c),
        |  .o(o),
        |  .o_v(o_v)
        |);
        """.stripMargin
      )

    val result = emit(bb, main, None, TranslationOptions())

    result shouldNot containStr ( "import chisel3.util.HasBlackBoxResource")
    result should containStr ( "class my_black_box() extends BlackBox {")
    result should containStr (
      "val io = IO(new Bundle {",
        "val i_a = Input(UInt(8.W))",
        "val i_b = Input(UInt(8.W))",
        "val i_c = Input(Bool())",
        "val o = Output(UInt(8.W))",
        "val o_v = Output(UInt(8.W))",
      "})"
    )

    result should containStr (
      "val inst = Module(new my_black_box)",
      "inst.io.i_a := i_a",
      "inst.io.i_b := i_b",
      "inst.io.i_c := i_c",
      "o := inst.io.o",
      "o_v := inst.io.o_v"
    )
  }

  it should "work with vec ports" in {

    val bb = wrapInModule("""
        |input  [3:0][7:0] i;
        |output [3:0][7:0] o;
        |
        |assign o = i;
        """.stripMargin,
        "my_black_box"
      )

    val main = wrapInModule("""
        |input  [3:0][7:0] i;
        |output [3:0][7:0] o;
        |
        |my_black_box inst(
        |  .i(i),
        |  .o(o)
        |);
        """.stripMargin
      )

    val result = emit(bb, main, None, TranslationOptions())

    result shouldNot containStr ( "import chisel3.util.HasBlackBoxResource")
    result should containStr ( "class my_black_box() extends RawModule {",
      "override def desiredName = \"my_black_boxWrapper\"",
      "",
      "val io = IO(new Bundle {",
        "val i = Input(Vec(4, UInt(8.W)))",
        "val o = Output(Vec(4, UInt(8.W)))",
      "})"
    )

    result should containStr (
      "val inst = Module(new my_black_box)",
      "inst.io.i := i",
      "o := inst.io.o"
    )
    result should containStr ( "class my_black_boxBB() extends BlackBox {",
      "override def desiredName = \"my_black_box\"",
      "val io = IO(new Bundle {",
        "val i = Input(UInt((4*8).W))",
        "val o = Output(UInt((4*8).W))",
      "})"
    )
  }

  it should "work with parameters and ports functional-style" in {

    val bb = """
          |module my_black_box#(
          |  parameter TESTI = 1,
          |  parameter TESTS = "TRUE",
          |  parameter TEST
          |)(
          |  input a,
          |  output b
          |);
          |assign b = TEST ? a : '0;
          |endmodule
        """.stripMargin

    val main = wrapInModule("""
        |input  a;
        |output b;
        |
        |my_black_box #(.TEST(1)) inst (
        |  .a(a),
        |  .b(b)
        |);
        """.stripMargin
      )

    val result = emit(bb, main, None, TranslationOptions())
    val q = "\""
    result should containStr (
       "class my_black_box(",
           "val TESTI: Int = 1,",
          s"val TESTS: String = ${q}TRUE$q,",
           "val TEST: Int",
         ") extends BlackBox(Map(",
                     s"${q}TESTI$q -> TESTI,",

                     s"${q}TEST$q -> TEST",
         ")) {",
         "val io = IO(new Bundle {",
           "val a = Input(Bool())",
           "val b = Output(Bool())",
         "})",
       "}"
    )

    result should containStr (
      "val inst = Module(new my_black_box(",
          "TEST = 1",
      "))",
      "inst.io.a := a",
      "b := inst.io.b"
    )
  }

  it should "support explicit hardware parameters" in {
    val p = wrapInPackage(s"""
          |localparam WIDTH = 5;
          |localparam logic [WIDTH-1:0] INIT_VALUE = '0;
      """.stripMargin, "test_p"
    )
    val inner = """
          |
          |import test_p::WIDTH;
          |
          |module my_module #(
          |  parameter logic [WIDTH-1:0] INIT_VALUE = '0,
          |  parameter TEST
          |)(
          |  input a,
          |  output b
          |);
          |assign b = TEST ? a : '0;
          |endmodule
        """.stripMargin

    val main = wrapInModule("""
          |input  a;
          |input  b;
          |
          |my_module #(.INIT_VALUE('0), .TEST(0)) inst(
          |  .a(a),
          |  .b(b)
          |);
        """.stripMargin)

    val result = emit(inner, p + main, None, TranslationOptions())
    debug(result)
    result should containStr ("import chisel3._")

    result should containStr (
      "package object test_p {",
        "",
        "val WIDTH = 5",
        "val INIT_VALUE: UInt = 0.U",
        "",
      "}"
    )

    result should containStr ("import test_p.WIDTH")

    result should containStr (
      "class my_module(",
          "val INIT_VALUE: UInt = 0.U,", // probably not the best style but compilable
          "val TEST: Int",
        ") extends BlackBox(Map(",
                  "\"INIT_VALUE\" -> INIT_VALUE.litValue,", // thanks to litValue
                  "\"TEST\" -> TEST",
        ")) {",
        "val io = IO(new Bundle {",
          "val a = Input(Bool())",
          "val b = Output(Bool())",
        "})"
    )

    result should containStr (
      "val inst = Module(new my_module(",
          "INIT_VALUE = 0.U,",
          "TEST = 0",
      "))",
      "inst.io.a := a",
      "b := inst.io.b"
    )

  }

}


// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2020 The sv2chisel Authors. All rights reserved.

package sv2chiselTests

import sv2chiselTests.utils._
import logger._

class DefLogicSpecs extends Sv2ChiselSpec {
  Logger.setLevel(LogLevel.Warn)

  behavior of "InferDefLogic"

  it should "emit RegInit properly" in {
    val result = emitInModule(s"""
      |localparam WWW = 1;
      |localparam WW = 1;
      |
      |wire clk;
      |wire          change;
      |reg [WW-1:0]  counter = '0;
      |reg [WWW-1:0] current = '1;
      |
      |always @(posedge clk) begin
      |    if (change) begin
      |        if (current < (1<<WWW)-1) begin
      |            current <= current + 1;
      |        end else begin
      |            current <= 0;
      |        end
      |        counter <= 0;
      |    end else begin
      |        counter <= counter + 1;
      |    end
      |end
      """.stripMargin
    )
    result should containStr ("class Test() extends Module {")

    result should containStr ("val WWW = 1")
    result should containStr ("val change = Wire(Bool())")
    result should containStr ("val counter = RegInit(UInt(WW.W), 0.U)")
    result should containStr ("val current = RegInit(UInt(WWW.W), ((1 << WWW)-1).U)")

    // TO DO
    result should containStr ("when(current < ((1 << WWW)-1).U) {")
    result should containStr ("current := current+1.U")

  }

  it should "legalize output reg" in {
    val result = emit(s"""
      |module my_module #(
      |    parameter WIDTH = 3
      |)(
      |    input clk,

      |    output reg [WIDTH-1:0] counter = '0
      |);
      |
      |    always @(posedge clk) begin
      |        if (rst) begin
      |            counter <= 0;
      |        end else begin
      |            counter <= counter + 1;
      |        end
      |    end
      |endmodule

      """.stripMargin
    )
    result should containStr ("class my_module(" )
    result should containStr ("val WIDTH: Int = 3" )
    result should containStr (") extends Module {")
    result should containStr ("val rst = IO(Input(Bool()))") // clock is abstracted but rst is not
    result should containStr ("val counter = IO(Output(UInt(WIDTH.W)))")

    result should containStr ("// NOTE: The following statements are auto generated based on existing output reg of the original verilog source")
    result should containStr ("val counter_out_reg = RegInit(UInt(WIDTH.W), 0.U)")
    result should containStr ("counter := counter_out_reg")

    result should containStr ("when(rst) {")
    result should containStr (  "counter_out_reg := 0.U")
    result should containStr ("} .otherwise {")
    result should containStr (  "counter_out_reg := counter_out_reg+1.U")
    result should containStr ("}")

  }

}


// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2020 The sv2chisel Authors. All rights reserved.

package sv2chiselTests

import sv2chiselTests.utils._
import logger._

class InstanceFlowSpec extends Sv2ChiselSpec {
  Logger.setLevel(LogLevel.Warn)

  "Instance Concats" should "be properly emitted" in {
    val result = emitInModule(s"""
      |// might containStr more than the RAM due to moves
      |localparam DBLW = 2;
      |localparam WWW = 16;
      |wire [DBLW-1:0]   inst_in_u1;
      |wire [WWW-1:0]    inst_in_u2;
      |wire [WWW-1:0]    inst_in_u3;
      |wire              inst_in_b;
      |
      |assign inst_in_u1 = '0;
      |assign inst_in_u2 = '1;
      |assign inst_in_u3 = inst_in_u2;
      |assign inst_in_b = '1;
      |
      |wire [DBLW-1:0]   inst_out_u1;
      |wire [WWW-1:0]    inst_out_u2;
      |wire [WWW-1:0]    inst_out_u3;
      |wire              inst_out_b;
      |mod inst(
      |  .in(inst_in_u3),
      |  .din({inst_in_u1, inst_in_b, inst_in_u2}),
      |  .dout({inst_out_u1, inst_out_b, inst_out_u2}),
      |  .out(inst_out_u3)
      |);
      |wire [DBLW-1:0]   post_out_u1;
      |wire [WWW-1:0]    post_out_u2;
      |wire [WWW-1:0]    post_out_u3;

    result should containStr ("class Test() extends RawModule {")

    result should containStr ("inst.in := inst_in_u3")

    result should containStr (
      "val inst_din = Wire(new Bundle {",
        "val inst_in_u1 = UInt(DBLW.W)",
        "val inst_in_b = Bool()",
        "val inst_in_u2 = UInt(WWW.W)",
      "})",
      "inst_din.inst_in_u1 := inst_in_u1",
      "inst_din.inst_in_b := inst_in_b",
      "inst_din.inst_in_u2 := inst_in_u2",
      "val inst_dout = Wire(new Bundle {",
        "val inst_out_u1 = UInt(DBLW.W)",
        "val inst_out_b = Bool()",
        "val inst_out_u2 = UInt(WWW.W)",
      "})",
    )

    result should containStr ("inst.din := inst_din.asTypeOf(inst.din)") // issue here
    result should containStr ("inst_dout := inst.dout.asTypeOf(inst_dout)")
    result should containStr ("inst_out_u3 := inst.out.asTypeOf(inst_out_u3)")
    // LHS bundle serialization
    result should containStr (
      "inst_out_u1 := inst_dout.inst_out_u1",
      "inst_out_b := inst_dout.inst_out_b",
      "inst_out_u2 := inst_dout.inst_out_u2"
    )
  }

}

      |wire              post_out_b;
      |
      |assign post_out_u1 = inst_out_u1;
      |assign post_out_u2 = inst_out_u2;
      |assign post_out_u3 = inst_out_u3;


// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2020 The sv2chisel Authors. All rights reserved.

package sv2chiselTests

import sv2chiselTests.utils._
import logger._

class LegalizeExpressionSpec extends Sv2ChiselSpec {
  Logger.setLevel(LogLevel.Warn)

  behavior of "LegalizeExpression"

  it should "manage general types properly" in {
    val result = emitInModule("""
      |localparam [0:0] B_TRUE = 1;
      |localparam [0:0] B_FALSE = 0;
      |localparam [0:0] C_UNKNOWN;
      |localparam integer INTP = (B_TRUE ? 5 : 4) + C_UNKNOWN*B_FALSE;
      |
      |wire a, b, c;
      |assign c = |{B_TRUE && a, (B_TRUE || B_FALSE) && b, C_UNKNOWN && b};
      |
      |wire [31:0] w;
      |wire [31:0] wu;
      |wire [31:0] z;
      |assign w[14:12] = 3'b000;
      |assign w[31:16] = $signed(w[10:0]);
      |assign w[15:0] = $signed(z[10:0]);
      |assign w[14:0] = $signed({z[5:0], z[7:6]});
      |assign wu = $signed(w);
      |assign w = z*z+z+1;
      |
      |wire [31:0] ww;
      |wire b;
      |assign ww[31:25] = w[6:5] == 2'b00 ? 7'b0100000 : 7'b0000000;
      |assign ww[31:25] = b ? 2'b01 : (b ? 2'b10 : 2'b00);
      """.stripMargin
    )
    debug(result)
    result should containStr ("class Test() extends RawModule {")
    result should containStr ("val B_TRUE: Boolean = true")
    result should containStr ("val B_FALSE: Boolean = false")
    result should containStr ("val C_UNKNOWN: Boolean")
    result should containStr ("val INTP = (if(B_TRUE) 5 else 4)+((if(C_UNKNOWN) 1 else 0)*(if(B_FALSE) 1 else 0))")
    result should containStr ("val a = Wire(Bool())")
    result should containStr ("val b = Wire(Bool())")
    result should containStr ("val c = Wire(Bool())")
    result should containStr ("c := Cat(B_TRUE.B && a, (B_TRUE.B || B_FALSE.B) && b, C_UNKNOWN.B && b).orR()")

    result should containStr ("val w = Wire(Vec(32, Bool()))")
    result should containStr ("val z = Wire(UInt(32.W))")
    result should containStr ("w(14,12) := \"b000\".U(3.W).asBools")
    /// padding (bit extension is done properly)
    result should containStr ("w(31,16) := w(10,0).asTypeOf(SInt(16.W)).asBools")
    result should containStr ("w(15,0) := z(10,0).asTypeOf(SInt(16.W)).asBools")
    result should containStr ("w(14,0) := Cat(z(5,0), z(7,6)).asTypeOf(SInt(15.W)).asBools")
    result should containStr ("wu := w.asTypeOf(SInt(32.W)).asUInt")
    result should containStr ("w := (((z*z)+z)+1.U).asTypeOf(Vec(32, Bool()))")

    result should containStr ("ww(31,25) := Mux(w(6,5).asUInt === \"b00\".U(2.W), \"b0100000\".U(7.W), \"b0000000\".U(7.W)).asBools")
    // ugly rendering but ... it works ...
    result should containStr ("ww(31,25) := Mux(b, \"b01\".U(2.W).asTypeOf(Vec(7, Bool())), (Mux(b, \"b10\".U(2.W), \"b00\".U(2.W)).asTypeOf(Vec(7, Bool()))))")
  }

  it should "also deal with mixed signed and concat" in {
    val result = emitInModule("""
      |wire [31:0] lhsc;
      |wire [31:0] wu;
      |assign {lhsc[15:0], lhsc[31:16]} = $signed(wu[10:0]);

                            "})")
    result should containStr ("auto_concat := wu(10,0).asTypeOf(SInt(32.W)).asTypeOf(auto_concat)")
    result should containStr ("lhsc(15,0) := auto_concat.lhsc_15_0")
    result should containStr ("lhsc(31,16) := auto_concat.lhsc_31_16")

  }

  it should "also deal with arithmetic shift right" in {
    val result = emitInModule("""
      |wire [16:0] a;
      |wire [31:0] res;
      |assign res = $signed(a) >>> 1;
      |assign res = $signed(a) >> 1;
      """.stripMargin
    )
    debug(result)
    result should containStr ("class Test() extends RawModule {")
    result should containStr ("val a = Wire(UInt(17.W))")
    result should containStr ("val res = Wire(UInt(32.W))")
    result should containStr ("res := (a.asTypeOf(SInt(32.W)) >> 1.U).asUInt")
    result should containStr ("res := a.asTypeOf(SInt(32.W)).asUInt >> 1")
  }

  it should "properly manage reduction operators" in {
    val result = emitInModule("""
      |wire [31:0] a;
      |wire [31:0] b;
      |wire res;
      |assign b[15:0] = '1;
      |assign res = |(a & ~b);
      """.stripMargin
    )
    debug(result)
    result should containStr ("class Test() extends RawModule {")
    result should containStr ("val a = Wire(UInt(32.W))")
    result should containStr ("val b = Wire(Vec(32, Bool()))")
    result should containStr ("val res = Wire(Bool())")
    result should containStr ("b(15,0) := 65535.U.asTypeOf(Vec(16, Bool()))")
    result should containStr ("res := (a&( ~b.asUInt)).orR()")

  }

  it should "properly manage bitwise operators on Bool" in {
    val result = emitInModule("""
      |wire a;
      |wire b;
      |wire c;
      |wire w, r, o;
      |assign w = a ^ b;
      |assign r = w ^ c;
      |assign o = (w & c) | (a & b);
      |assign o = (w && c) || (a && b);
      """.stripMargin
    )
    debug(result)
    result should containStr ("class Test() extends RawModule {")
    result should containStr ( "w := a^b" )
    result should containStr ( "r := w^c" )
    result should containStr ( "o := (w&c)|(a&b)" )
    result should containStr ( "o := (w && c) || (a && b)" )
  }

  it should "manage edge cases" in {
    val result = emitInModule("""
      |localparam PA = 2;
      |localparam PB = 2;
      |localparam PC = 6;
      |
      |typedef struct packed {
      |    logic [7:0] fieldA;
      |    logic [13:0] fieldB;
      |    logic [13:0] fieldC;
      |} my_struct_t;
      |
      |wire my_struct_t s;
      |// force vec bool fields
      |assign s.fieldA[0] = '1;
      |assign s.fieldB[0] = '1;
      |assign s.fieldC[0] = '1;
      |
      |wire v;
      |wire [7:0] u;
      |
      |assign u = (s.fieldA == PC ? PB : 0) + $bits(my_struct_t)/8;
      |assign u = s.fieldB + PA + (s.fieldA == PC ? PB : 0) + $bits(my_struct_t)/8;
      |
      |// NB:(Chisel error): can't create Mux with heterogeneous types class chisel3.SInt and class chisel3.UInt
      |assign v = s.fieldC >= (s.fieldB + PA + (s.fieldA == PC ? PB : 0) + $bits(my_struct_t)/8);
      |assign v = s.fieldC == (s.fieldB + PA + (s.fieldA == PC ? PB : 0) + $bits(my_struct_t)/8);
      """.stripMargin
    )
    debug(result)

    result should containStr ("val v = Wire(Bool())")
    result should containStr ("val u = Wire(UInt(8.W))")
    result should containStr (
      "u := (Mux(s.fieldA.asUInt === PC.U, PB.U(8.W), 0.U))+((new my_struct_t).getWidth/8).U",
      "u := ((s.fieldB.asUInt+PA.U)+(Mux(s.fieldA.asUInt === PC.U, PB.U(8.W), 0.U)))+((new my_struct_t).getWidth/8).U"
    )

    result should containStr (
      "v := s.fieldC.asUInt >= (((s.fieldB.asUInt+PA.U)+(Mux(s.fieldA.asUInt === PC.U, PB.U, 0.U)))+((new my_struct_t).getWidth/8).U)",
      "v := s.fieldC.asUInt === (((s.fieldB.asUInt+PA.U)+(Mux(s.fieldA.asUInt === PC.U, PB.U, 0.U)))+((new my_struct_t).getWidth/8).U)"
    )
  }

  it should "properly manage arithmetic operators on Vec[Bool]" in {
    val result = emitInModule("""
      |wire [7:0] a;
      |wire [7:0] b;
      |wire [7:0] c;
      |// useless assign to preserve Vec[Bool] type
      |assign a[3:0] = '0;
      |assign b[3:0] = '0;
      |assign c = a + b;
      """.stripMargin
    )
    debug(result)
    result should containStr ("val a = Wire(Vec(8, Bool()))")
    result should containStr ("val b = Wire(Vec(8, Bool()))")
    result should containStr ("val c = Wire(UInt(8.W))")

    result should containStr ("c := a.asUInt+b.asUInt")

  }
}

      """.stripMargin
    )
    debug(result)
    result should containStr ("class Test() extends RawModule {")
    result should containStr ("val lhsc = Wire(Vec(32, Bool()))")


// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2020 The sv2chisel Authors. All rights reserved.

package sv2chiselTests

import sv2chiselTests.utils._
import logger._

class MacroSpec extends Sv2ChiselSpec {
  Logger.setLevel(LogLevel.Warn)

  behavior of "MacroSpec"

  it should "pass lexing & parsing (TODO: produce valid Chisel)" in {
    val result = emitInModule(s"""
      |`define TRUC 32
      |localparam TRUC = 32;
      |`define CALC(VAL1, VAL2, VAL3) \\
      | RESULT = VAL1 EXPR VAL2; \\
      | $$display("Result is %0d", RESULT);
      |localparam CALC = TRUC;
      |`ifdef TRUC
      |wire [`TRUC-1:0] test = `TRUC'b1;
      |`else
      |wire [32-1:0] test = 32'b1;
      |`endif
      """.stripMargin
    )

    result should containStr ("val TRUC = 32")
    result should containStr ("val CALC = TRUC")


// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2020 The sv2chisel Authors. All rights reserved.

package sv2chiselTests

import sv2chiselTests.utils._
import logger._

class RemoveConcatSpec extends Sv2ChiselSpec {
  Logger.setLevel(LogLevel.Warn)

  behavior of "RemoveConcats"

      |
      |wire                     bool;
      |wire [DBLW-1:0][WWW:0]   pack;
      |wire [DBLW-1:0][WWW-1:0] packsmall;
      |
      |generate
      |  for (i = 0; i < DBLW/2; i++) begin: loop
      |    assign {bool, packsmall[i][WWW-1:1], packsmall[i][0]} = pack[2*i];
      |    assign pack[2*i+1] = bool ? {packsmall[i][WWW-1:0], 1'b0} : '0;
      |  end
      |endgenerate
      """.stripMargin
    )
    debug(result)
    result should containStr ("class Test() extends RawModule {")
    result should containStr ("val auto_concat = Wire(new Bundle {",
                              "val bool = Bool()",
                              "val packsmall_i_WWW_1_1 = Vec(WWW-1, Bool())",
                              "val packsmall_i_0 = Bool()",
                            "})",
                            "auto_concat := pack(2*i).asTypeOf(auto_concat)",
                            "bool := auto_concat.bool",
                            "packsmall(i)(WWW-1,1) := auto_concat.packsmall_i_WWW_1_1",
                            "packsmall(i)(0) := auto_concat.packsmall_i_0")

    // seccond concat inline
    result should containStr ("pack((2*i)+1) := Mux(bool, Cat(packsmall(i)(WWW-1,0).asUInt, \"b0\".U(1.W)), 0.U)")

  }

  it should "properly emit Cat with subrange and replicate patterns" in {
    val result = emitInModule(s"""
      |localparam WWW = 16;
      |localparam AAA = 4;
      |
      |wire [WWW-1:0] w;
      |wire [WWW-1:0] y;
      |
      |assign w = {{(WWW-AAA){1'b0}}, y[AAA-1:0]};
      """.stripMargin
    )
    debug(result)
    result should containStr ("import chisel3._")
    result should containStr ("import sv2chisel.helpers.vecconvert._")
    result should containStr ("import chisel3.util.Cat")

    result should containStr ("class Test() extends RawModule {")
    result should containStr (
                            "val w = Wire(UInt(WWW.W))",
                            "val y = Wire(Vec(WWW, Bool()))",
                            "w := Cat((VecInit.tabulate((WWW-AAA))(_ => false.B)).asUInt, y(AAA-1,0).asUInt)",
                          )

  }

  it should "properly emit Cat with subrange and replicate patterns in functions" in {
    val result = emit(wrapInPackage(s"""
      |localparam WWW = 16;
      |localparam AAA = 4;
      |function  [WWW-1:0] w;
      |  input logic [WWW-1:0] y;
      |  w = {{(WWW-AAA){1'b0}}, y[AAA-1:0]};
      |endfunction;
      """.stripMargin
    ))
    debug(result)
    result should containStr ("import chisel3._")
    result should containStr ("import sv2chisel.helpers.vecconvert._")
    result should containStr ("import chisel3.util.Cat")

    result should containStr (
                            "def w(y:Vec[Bool]): UInt = {",
                            "Cat((VecInit.tabulate((WWW-AAA))(_ => false.B)).asUInt, y(AAA-1,0).asUInt)",
                            "}"
                          )

  }

}

  it should "properly remove concats or use Cat" in {
    val result = emitInModule(s"""
      |// might containStr more than the RAM due to moves


// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2020 The sv2chisel Authors. All rights reserved.

package sv2chiselTests

import sv2chiselTests.utils._
import sv2chisel.{TranslationOptions, ChiselizerOptions}
import logger._

class UnpackedEmissionStyleSpec extends Sv2ChiselSpec {
  Logger.setLevel(LogLevel.Warn)

  // Basic Tests of Module wraper for tests
  behavior of "UnpackedEmissionStyle"

  val baseContent = wrapInModule(
    s"""
      |//Verilog wire
      |wire [1:0] w[3:0];
      |assign w[3] = '0;
      |assign w[2] = '1;
      |assign w[1] = '1;
      |assign w[0] = '0;
      |// verilog register
      |input clk;
      |reg [1:0] r[3:0];
      |always @(posedge clk) begin
      |  r[3] <= '0;
      |  r[2] <= '1;
      |  r[1] <= '1;
      |  r[0] <= '0;
      |end
    """.stripMargin
  )

  it should "support unpacked wire & reg with UnpackedEmissionStyle.Reg" in {
    val chiselizerOpts = ChiselizerOptions().copy(
      unpackedEmissionStyle = ChiselizerOptions.UnpackedEmissionStyle.Reg
    )
    val result = emit(baseContent,
      options = TranslationOptions(chiselizer = chiselizerOpts)
    )
    debug(result)
    result should containStr (

      "val r = Reg(Vec(4, UInt(2.W)))",
      "r(3) := 0.U",
      "r(2) := 3.U",
      "r(1) := 3.U",
      "r(0) := 0.U",
    )
  }

  it should "support unpacked wire & reg with UnpackedEmissionStyle.Mem" in {
    val chiselizerOpts = ChiselizerOptions().copy(
      unpackedEmissionStyle = ChiselizerOptions.UnpackedEmissionStyle.Mem
    )
    val result = emit(baseContent,
      options = TranslationOptions(chiselizer = chiselizerOpts)
    )
    debug(result)
    result should containStr (
      "val w = Wire(Vec(4, UInt(2.W)))",
      "w(3) := 0.U",
      "w(2) := 3.U",
      "w(1) := 3.U",
      "w(0) := 0.U"
      )
    result should containStr (
      "val r = Mem(4,UInt(2.W))",
      "r(3) := 0.U",
      "r(2) := 3.U",
      "r(1) := 3.U",
      "r(0) := 0.U",
    )
  }
}


//******************************************************************************
// Copyright (c) 2015 - 2018, The Regents of the University of California (Regents).
// All Rights Reserved. See LICENSE and LICENSE.SiFive for license details.
//------------------------------------------------------------------------------

package boom.v3.exu

import chisel3._
import chisel3.util._
import chisel3.experimental.dataview._
import org.chipsalliance.cde.config.Parameters
import freechips.rocketchip.tile.FPConstants._
import freechips.rocketchip.tile.{FPUCtrlSigs, HasFPUParameters}
import freechips.rocketchip.tile
import freechips.rocketchip.rocket
import freechips.rocketchip.util.uintToBitPat
import boom.v3.common._
import boom.v3.util.{ImmGenRm, ImmGenTyp}

/**
 * FP Decoder for the FPU
 *
 * TODO get rid of this decoder and move into the Decode stage? Or the RRd stage?
 * most of these signals are already created, just need to be translated
 * to the Rocket FPU-speak
 */
class UOPCodeFPUDecoder(implicit p: Parameters) extends BoomModule with HasFPUParameters
{
  val io = IO(new Bundle {
    val uopc = Input(Bits(UOPC_SZ.W))
    val sigs = Output(new FPUCtrlSigs())
  })

  // TODO change N,Y,X to BitPat("b1"), BitPat("b0"), and BitPat("b?")
  val N = false.B
  val Y = true.B
  val X = false.B

  val default: List[BitPat] = List(X,X,X,X,X, X,X,X,X,X,X,X, X,X,X,X)

  val f_table: Array[(BitPat, List[BitPat])] =
    // Note: not all of these signals are used or necessary, but we're
    // constrained by the need to fit the rocket.FPU units' ctrl signals.
    //                                     swap12         fma
    //                                     | swap32       | div
    //                                     | | typeTagIn  | | sqrt
    //                          ldst       | | | typeTagOut | | wflags
    //                          | wen      | | | | from_int | | |
    //                          | | ren1   | | | | | to_int | | |
    //                          | | | ren2 | | | | | | fastpipe |
    //                          | | | | ren3 | | | | | |  | | | |
    //                          | | | | |  | | | | | | |  | | | |
    Array(
    BitPat(uopFCLASS_S) -> List(X,X,Y,N,N, N,X,S,S,N,Y,N, N,N,N,N),
    BitPat(uopFMV_W_X)  -> List(X,X,N,N,N, X,X,S,D,Y,N,N, N,N,N,N),
    BitPat(uopFMV_X_W)  -> List(X,X,Y,N,N, N,X,D,S,N,Y,N, N,N,N,N),

    BitPat(uopFCVT_S_X) -> List(X,X,N,N,N, X,X,S,S,Y,N,N, N,N,N,Y),

    BitPat(uopFCVT_X_S) -> List(X,X,Y,N,N, N,X,S,S,N,Y,N, N,N,N,Y),

    BitPat(uopCMPR_S)   -> List(X,X,Y,Y,N, N,N,S,S,N,Y,N, N,N,N,Y),

    BitPat(uopFSGNJ_S)  -> List(X,X,Y,Y,N, N,N,S,S,N,N,Y, N,N,N,N),

    BitPat(uopFMINMAX_S)-> List(X,X,Y,Y,N, N,N,S,S,N,N,Y, N,N,N,Y),

    BitPat(uopFADD_S)   -> List(X,X,Y,Y,N, N,Y,S,S,N,N,N, Y,N,N,Y),
    BitPat(uopFSUB_S)   -> List(X,X,Y,Y,N, N,Y,S,S,N,N,N, Y,N,N,Y),
    BitPat(uopFMUL_S)   -> List(X,X,Y,Y,N, N,N,S,S,N,N,N, Y,N,N,Y),
    BitPat(uopFMADD_S)  -> List(X,X,Y,Y,Y, N,N,S,S,N,N,N, Y,N,N,Y),
    BitPat(uopFMSUB_S)  -> List(X,X,Y,Y,Y, N,N,S,S,N,N,N, Y,N,N,Y),
    BitPat(uopFNMADD_S) -> List(X,X,Y,Y,Y, N,N,S,S,N,N,N, Y,N,N,Y),
    BitPat(uopFNMSUB_S) -> List(X,X,Y,Y,Y, N,N,S,S,N,N,N, Y,N,N,Y)
    )

  val d_table: Array[(BitPat, List[BitPat])] =
    Array(
    BitPat(uopFCLASS_D) -> List(X,X,Y,N,N, N,X,D,D,N,Y,N, N,N,N,N),
    BitPat(uopFMV_D_X)  -> List(X,X,N,N,N, X,X,D,D,Y,N,N, N,N,N,N),
    BitPat(uopFMV_X_D)  -> List(X,X,Y,N,N, N,X,D,D,N,Y,N, N,N,N,N),
    BitPat(uopFCVT_S_D) -> List(X,X,Y,N,N, N,X,D,S,N,N,Y, N,N,N,Y),
    BitPat(uopFCVT_D_S) -> List(X,X,Y,N,N, N,X,S,D,N,N,Y, N,N,N,Y),

    BitPat(uopFCVT_D_X) -> List(X,X,N,N,N, X,X,D,D,Y,N,N, N,N,N,Y),

    BitPat(uopFCVT_X_D) -> List(X,X,Y,N,N, N,X,D,D,N,Y,N, N,N,N,Y),

    BitPat(uopCMPR_D)   -> List(X,X,Y,Y,N, N,N,D,D,N,Y,N, N,N,N,Y),

    BitPat(uopFSGNJ_D)  -> List(X,X,Y,Y,N, N,N,D,D,N,N,Y, N,N,N,N),

    BitPat(uopFMINMAX_D)-> List(X,X,Y,Y,N, N,N,D,D,N,N,Y, N,N,N,Y),

    BitPat(uopFADD_D)   -> List(X,X,Y,Y,N, N,Y,D,D,N,N,N, Y,N,N,Y),
    BitPat(uopFSUB_D)   -> List(X,X,Y,Y,N, N,Y,D,D,N,N,N, Y,N,N,Y),
    BitPat(uopFMUL_D)   -> List(X,X,Y,Y,N, N,N,D,D,N,N,N, Y,N,N,Y),

    BitPat(uopFMADD_D)  -> List(X,X,Y,Y,Y, N,N,D,D,N,N,N, Y,N,N,Y),
    BitPat(uopFMSUB_D)  -> List(X,X,Y,Y,Y, N,N,D,D,N,N,N, Y,N,N,Y),
    BitPat(uopFNMADD_D) -> List(X,X,Y,Y,Y, N,N,D,D,N,N,N, Y,N,N,Y),
    BitPat(uopFNMSUB_D) -> List(X,X,Y,Y,Y, N,N,D,D,N,N,N, Y,N,N,Y)
    )

//   val insns = fLen match {
//      case 32 => f_table
//      case 64 => f_table ++ d_table
//   }
  val insns = f_table ++ d_table
  val decoder = rocket.DecodeLogic(io.uopc, default, insns)

  val s = io.sigs
  val sigs = Seq(s.ldst, s.wen, s.ren1, s.ren2, s.ren3, s.swap12,
                 s.swap23, s.typeTagIn, s.typeTagOut, s.fromint, s.toint, s.fastpipe, s.fma,
                 s.div, s.sqrt, s.wflags)
  sigs zip decoder map {case(s,d) => s := d}
  s.vec := false.B
}

/**
 * FP fused multiple add decoder for the FPU
 */
class FMADecoder extends Module
{
  val io = IO(new Bundle {
    val uopc = Input(UInt(UOPC_SZ.W))
    val cmd = Output(UInt(2.W))
  })

  val default: List[BitPat] = List(BitPat("b??"))
  val table: Array[(BitPat, List[BitPat])] =
    Array(
      BitPat(uopFADD_S)   -> List(BitPat("b00")),
      BitPat(uopFSUB_S)   -> List(BitPat("b01")),
      BitPat(uopFMUL_S)   -> List(BitPat("b00")),
      BitPat(uopFMADD_S)  -> List(BitPat("b00")),
      BitPat(uopFMSUB_S)  -> List(BitPat("b01")),
      BitPat(uopFNMADD_S) -> List(BitPat("b11")),
      BitPat(uopFNMSUB_S) -> List(BitPat("b10")),
      BitPat(uopFADD_D)   -> List(BitPat("b00")),
      BitPat(uopFSUB_D)   -> List(BitPat("b01")),
      BitPat(uopFMUL_D)   -> List(BitPat("b00")),
      BitPat(uopFMADD_D)  -> List(BitPat("b00")),
      BitPat(uopFMSUB_D)  -> List(BitPat("b01")),
      BitPat(uopFNMADD_D) -> List(BitPat("b11")),
      BitPat(uopFNMSUB_D) -> List(BitPat("b10"))
      )

  val decoder = rocket.DecodeLogic(io.uopc, default, table)

  val (cmd: UInt) :: Nil = decoder
  io.cmd := cmd
}

/**
 * Bundle representing data to be sent to the FPU
 */

{
  val uop      = new MicroOp()
  val rs1_data = Bits(65.W)
  val rs2_data = Bits(65.W)
  val rs3_data = Bits(65.W)
  val fcsr_rm  = Bits(tile.FPConstants.RM_SZ.W)
}

/**
 * FPU unit that wraps the RocketChip FPU units (which in turn wrap hardfloat)
 */
class FPU(implicit p: Parameters) extends BoomModule with tile.HasFPUParameters
{
  val io = IO(new Bundle {
    val req = Flipped(new ValidIO(new FpuReq))
    val resp = new ValidIO(new ExeUnitResp(65))
  })
  io.resp.bits := DontCare

  // all FP units are padded out to the same latency for easy scheduling of the write port
  val fpu_latency = dfmaLatency
  val io_req = io.req.bits

  val fp_decoder = Module(new UOPCodeFPUDecoder)
  fp_decoder.io.uopc := io_req.uop.uopc
  val fp_ctrl = fp_decoder.io.sigs
  val fp_rm = Mux(ImmGenRm(io_req.uop.imm_packed) === 7.U, io_req.fcsr_rm, ImmGenRm(io_req.uop.imm_packed))

  def fuInput(minT: Option[tile.FType]): tile.FPInput = {
    val req = Wire(new tile.FPInput)
    val tag = fp_ctrl.typeTagIn
    req.viewAsSupertype(new tile.FPUCtrlSigs) := fp_ctrl
    req.rm := fp_rm
    req.in1 := unbox(io_req.rs1_data, tag, minT)
    req.in2 := unbox(io_req.rs2_data, tag, minT)
    req.in3 := unbox(io_req.rs3_data, tag, minT)
    when (fp_ctrl.swap23) { req.in3 := req.in2 }
    req.typ := ImmGenTyp(io_req.uop.imm_packed)
    req.fmt := Mux(tag === S, 0.U, 1.U) // TODO support Zfh and avoid special-case below
    when (io_req.uop.uopc === uopFMV_X_W) {
      req.fmt := 0.U
    }

    val fma_decoder = Module(new FMADecoder)
    fma_decoder.io.uopc := io_req.uop.uopc
    req.fmaCmd := fma_decoder.io.cmd // ex_reg_inst(3,2) | (!fp_ctrl.ren3 && ex_reg_inst(27))
    req
  }

  val dfma = Module(new tile.FPUFMAPipe(latency = fpu_latency, t = tile.FType.D))
  dfma.io.in.valid := io.req.valid && fp_ctrl.fma && (fp_ctrl.typeTagOut === D)
  dfma.io.in.bits := fuInput(Some(dfma.t))

  val sfma = Module(new tile.FPUFMAPipe(latency = fpu_latency, t = tile.FType.S))
  sfma.io.in.valid := io.req.valid && fp_ctrl.fma && (fp_ctrl.typeTagOut === S)
  sfma.io.in.bits := fuInput(Some(sfma.t))

  val fpiu = Module(new tile.FPToInt)
  fpiu.io.in.valid := io.req.valid && (fp_ctrl.toint || (fp_ctrl.fastpipe && fp_ctrl.wflags))
  fpiu.io.in.bits := fuInput(None)
  val fpiu_out = Pipe(RegNext(fpiu.io.in.valid && !fp_ctrl.fastpipe),
                              fpiu.io.out.bits, fpu_latency-1)
  val fpiu_result  = Wire(new tile.FPResult)
  fpiu_result.data := fpiu_out.bits.toint
  fpiu_result.exc  := fpiu_out.bits.exc

  val fpmu = Module(new tile.FPToFP(fpu_latency)) // latency 2 for rocket
  fpmu.io.in.valid := io.req.valid && fp_ctrl.fastpipe
  fpmu.io.in.bits := fpiu.io.in.bits
  fpmu.io.lt := fpiu.io.out.bits.lt
  val fpmu_double = Pipe(io.req.valid && fp_ctrl.fastpipe, fp_ctrl.typeTagOut === D, fpu_latency).bits

  // Response (all FP units have been padded out to the same latency)
  io.resp.valid := fpiu_out.valid ||
                   fpmu.io.out.valid ||
                   sfma.io.out.valid ||
                   dfma.io.out.valid
  val fpu_out_data =
    Mux(dfma.io.out.valid, box(dfma.io.out.bits.data, true.B),
    Mux(sfma.io.out.valid, box(sfma.io.out.bits.data, false.B),
    Mux(fpiu_out.valid,    fpiu_result.data,
                           box(fpmu.io.out.bits.data, fpmu_double))))

  val fpu_out_exc =
    Mux(dfma.io.out.valid, dfma.io.out.bits.exc,
    Mux(sfma.io.out.valid, sfma.io.out.bits.exc,
    Mux(fpiu_out.valid,    fpiu_result.exc,
                           fpmu.io.out.bits.exc)))

  io.resp.bits.data              := fpu_out_data
  io.resp.bits.fflags.valid      := io.resp.valid
  io.resp.bits.fflags.bits.flags := fpu_out_exc
}

class FpuReq()(implicit p: Parameters) extends BoomBundle


//******************************************************************************
// Copyright (c) 2013 - 2018, The Regents of the University of California (Regents).
// All Rights Reserved. See LICENSE and LICENSE.SiFive for license details.
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Functional Units
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//
// If regfile bypassing is disabled, then the functional unit must do its own
// bypassing in here on the WB stage (i.e., bypassing the io.resp.data)
//
// TODO: explore possibility of conditional IO fields? if a branch unit... how to add extra to IO in subclass?

package boom.v3.exu

import chisel3._
import chisel3.util._
import chisel3.experimental.dataview._

import org.chipsalliance.cde.config.Parameters
import freechips.rocketchip.util._
import freechips.rocketchip.tile
import freechips.rocketchip.rocket.{PipelinedMultiplier,BP,BreakpointUnit,Causes,CSR}

import boom.v3.common._
import boom.v3.ifu._
import boom.v3.util._

/**t
 * Functional unit constants
 */
object FUConstants
{
  // bit mask, since a given execution pipeline may support multiple functional units
  val FUC_SZ = 10
  val FU_X   = BitPat.dontCare(FUC_SZ)
  val FU_ALU =   1.U(FUC_SZ.W)
  val FU_JMP =   2.U(FUC_SZ.W)
  val FU_MEM =   4.U(FUC_SZ.W)
  val FU_MUL =   8.U(FUC_SZ.W)
  val FU_DIV =  16.U(FUC_SZ.W)
  val FU_CSR =  32.U(FUC_SZ.W)
  val FU_FPU =  64.U(FUC_SZ.W)
  val FU_FDV = 128.U(FUC_SZ.W)
  val FU_I2F = 256.U(FUC_SZ.W)
  val FU_F2I = 512.U(FUC_SZ.W)

  // FP stores generate data through FP F2I, and generate address through MemAddrCalc
  val FU_F2IMEM = 516.U(FUC_SZ.W)
}
import FUConstants._

/**
 * Class to tell the FUDecoders what units it needs to support
 *
 * @param alu support alu unit?
 * @param bru support br unit?
 * @param mem support mem unit?
 * @param muld support multiple div unit?
 * @param fpu support FP unit?
 * @param csr support csr writing unit?
 * @param fdiv support FP div unit?
 * @param ifpu support int to FP unit?
 */
class SupportedFuncUnits(
  val alu: Boolean  = false,
  val jmp: Boolean  = false,
  val mem: Boolean  = false,
  val muld: Boolean = false,
  val fpu: Boolean  = false,
  val csr: Boolean  = false,
  val fdiv: Boolean = false,
  val ifpu: Boolean = false)
{
}

/**
 * Bundle for signals sent to the functional unit
 *
 * @param dataWidth width of the data sent to the functional unit
 */
class FuncUnitReq(val dataWidth: Int)(implicit p: Parameters) extends BoomBundle
  with HasBoomUOP
{
  val numOperands = 3

  val rs1_data = UInt(dataWidth.W)
  val rs2_data = UInt(dataWidth.W)
  val rs3_data = UInt(dataWidth.W) // only used for FMA units
  val pred_data = Bool()

  val kill = Bool() // kill everything
}

/**
 * Bundle for the signals sent out of the function unit
 *
 * @param dataWidth data sent from the functional unit
 */
class FuncUnitResp(val dataWidth: Int)(implicit p: Parameters) extends BoomBundle
  with HasBoomUOP
{
  val predicated = Bool() // Was this response from a predicated-off instruction
  val data = UInt(dataWidth.W)
  val fflags = new ValidIO(new FFlagsResp)
  val addr = UInt((vaddrBits+1).W) // only for maddr -> LSU
  val mxcpt = new ValidIO(UInt((freechips.rocketchip.rocket.Causes.all.max+2).W)) //only for maddr->LSU
  val sfence = Valid(new freechips.rocketchip.rocket.SFenceReq) // only for mcalc
}

/**
 * Branch resolution information given from the branch unit
 */

{
  val uop        = new MicroOp
  val valid      = Bool()
  val mispredict = Bool()
  val taken      = Bool()                     // which direction did the branch go?
  val cfi_type   = UInt(CFI_SZ.W)

  // Info for recalculating the pc for this branch
  val pc_sel     = UInt(2.W)

  val jalr_target = UInt(vaddrBitsExtended.W)
  val target_offset = SInt()
}

class BrUpdateInfo(implicit p: Parameters) extends BoomBundle
{
  // On the first cycle we get masks to kill registers
  val b1 = new BrUpdateMasks
  // On the second cycle we get indices to reset pointers
  val b2 = new BrResolutionInfo
}

class BrUpdateMasks(implicit p: Parameters) extends BoomBundle
{
  val resolve_mask = UInt(maxBrCount.W)
  val mispredict_mask = UInt(maxBrCount.W)
}

/**
 * Abstract top level functional unit class that wraps a lower level hand made functional unit
 *
 * @param isPipelined is the functional unit pipelined?
 * @param numStages how many pipeline stages does the functional unit have
 * @param numBypassStages how many bypass stages does the function unit have
 * @param dataWidth width of the data being operated on in the functional unit
 * @param hasBranchUnit does this functional unit have a branch unit?
 */
abstract class FunctionalUnit(
  val isPipelined: Boolean,
  val numStages: Int,
  val numBypassStages: Int,
  val dataWidth: Int,
  val isJmpUnit: Boolean = false,
  val isAluUnit: Boolean = false,
  val isMemAddrCalcUnit: Boolean = false,
  val needsFcsr: Boolean = false)
  (implicit p: Parameters) extends BoomModule
{
  val io = IO(new Bundle {
    val req    = Flipped(new DecoupledIO(new FuncUnitReq(dataWidth)))
    val resp   = (new DecoupledIO(new FuncUnitResp(dataWidth)))

    val brupdate = Input(new BrUpdateInfo())

    val bypass = Output(Vec(numBypassStages, Valid(new ExeUnitResp(dataWidth))))

    // only used by the fpu unit
    val fcsr_rm = if (needsFcsr) Input(UInt(tile.FPConstants.RM_SZ.W)) else null

    // only used by branch unit
    val brinfo     = if (isAluUnit) Output(new BrResolutionInfo()) else null
    val get_ftq_pc = if (isJmpUnit) Flipped(new GetPCFromFtqIO()) else null
    val status     = if (isMemAddrCalcUnit) Input(new freechips.rocketchip.rocket.MStatus()) else null

    // only used by memaddr calc unit
    val bp = if (isMemAddrCalcUnit) Input(Vec(nBreakpoints, new BP)) else null
    val mcontext = if (isMemAddrCalcUnit) Input(UInt(coreParams.mcontextWidth.W)) else null
    val scontext = if (isMemAddrCalcUnit) Input(UInt(coreParams.scontextWidth.W)) else null

  })

  io.bypass.foreach { b => b.valid := false.B; b.bits := DontCare }

  io.resp.valid := false.B
  io.resp.bits := DontCare

  if (isJmpUnit) {
    io.get_ftq_pc.ftq_idx := DontCare
  }
}

/**
 * Abstract top level pipelined functional unit
 *
 * Note: this helps track which uops get killed while in intermediate stages,
 * but it is the job of the consumer to check for kills on the same cycle as consumption!!!
 *
 * @param numStages how many pipeline stages does the functional unit have
 * @param numBypassStages how many bypass stages does the function unit have
 * @param earliestBypassStage first stage that you can start bypassing from
 * @param dataWidth width of the data being operated on in the functional unit
 * @param hasBranchUnit does this functional unit have a branch unit?
 */
abstract class PipelinedFunctionalUnit(
  numStages: Int,
  numBypassStages: Int,
  earliestBypassStage: Int,
  dataWidth: Int,
  isJmpUnit: Boolean = false,
  isAluUnit: Boolean = false,
  isMemAddrCalcUnit: Boolean = false,
  needsFcsr: Boolean = false
  )(implicit p: Parameters) extends FunctionalUnit(
    isPipelined = true,
    numStages = numStages,
    numBypassStages = numBypassStages,
    dataWidth = dataWidth,
    isJmpUnit = isJmpUnit,
    isAluUnit = isAluUnit,
    isMemAddrCalcUnit = isMemAddrCalcUnit,
    needsFcsr = needsFcsr)
{
  // Pipelined functional unit is always ready.
  io.req.ready := true.B

  if (numStages > 0) {
    val r_valids = RegInit(VecInit(Seq.fill(numStages) { false.B }))
    val r_uops   = Reg(Vec(numStages, new MicroOp()))

    // handle incoming request
    r_valids(0) := io.req.valid && !IsKilledByBranch(io.brupdate, io.req.bits.uop) && !io.req.bits.kill
    r_uops(0)   := io.req.bits.uop
    r_uops(0).br_mask := GetNewBrMask(io.brupdate, io.req.bits.uop)

    // handle middle of the pipeline
    for (i <- 1 until numStages) {
      r_valids(i) := r_valids(i-1) && !IsKilledByBranch(io.brupdate, r_uops(i-1)) && !io.req.bits.kill
      r_uops(i)   := r_uops(i-1)
      r_uops(i).br_mask := GetNewBrMask(io.brupdate, r_uops(i-1))

      if (numBypassStages > 0) {
        io.bypass(i-1).bits.uop := r_uops(i-1)
      }
    }

    // handle outgoing (branch could still kill it)
    // consumer must also check for pipeline flushes (kills)
    io.resp.valid    := r_valids(numStages-1) && !IsKilledByBranch(io.brupdate, r_uops(numStages-1))
    io.resp.bits.predicated := false.B
    io.resp.bits.uop := r_uops(numStages-1)
    io.resp.bits.uop.br_mask := GetNewBrMask(io.brupdate, r_uops(numStages-1))

    // bypassing (TODO allow bypass vector to have a different size from numStages)
    if (numBypassStages > 0 && earliestBypassStage == 0) {
      io.bypass(0).bits.uop := io.req.bits.uop

      for (i <- 1 until numBypassStages) {
        io.bypass(i).bits.uop := r_uops(i-1)
      }
    }
  } else {
    require (numStages == 0)
    // pass req straight through to response

    // valid doesn't check kill signals, let consumer deal with it.
    // The LSU already handles it and this hurts critical path.
    io.resp.valid    := io.req.valid && !IsKilledByBranch(io.brupdate, io.req.bits.uop)
    io.resp.bits.predicated := false.B
    io.resp.bits.uop := io.req.bits.uop
    io.resp.bits.uop.br_mask := GetNewBrMask(io.brupdate, io.req.bits.uop)
  }
}

/**
 * Functional unit that wraps RocketChips ALU
 *
 * @param isBranchUnit is this a branch unit?
 * @param numStages how many pipeline stages does the functional unit have
 * @param dataWidth width of the data being operated on in the functional unit
 */
class ALUUnit(isJmpUnit: Boolean = false, numStages: Int = 1, dataWidth: Int)(implicit p: Parameters)
  extends PipelinedFunctionalUnit(
    numStages = numStages,
    numBypassStages = numStages,
    isAluUnit = true,
    earliestBypassStage = 0,
    dataWidth = dataWidth,
    isJmpUnit = isJmpUnit)
  with boom.v3.ifu.HasBoomFrontendParameters
{
  val uop = io.req.bits.uop

  // immediate generation
  val imm_xprlen = ImmGen(uop.imm_packed, uop.ctrl.imm_sel)

  // operand 1 select
  var op1_data: UInt = null
  if (isJmpUnit) {
    // Get the uop PC for jumps
    val block_pc = AlignPCToBoundary(io.get_ftq_pc.pc, icBlockBytes)
    val uop_pc = (block_pc | uop.pc_lob) - Mux(uop.edge_inst, 2.U, 0.U)

    op1_data = Mux(uop.ctrl.op1_sel.asUInt === OP1_RS1 , io.req.bits.rs1_data,
               Mux(uop.ctrl.op1_sel.asUInt === OP1_PC  , Sext(uop_pc, xLen),
                                                         0.U))
  } else {
    op1_data = Mux(uop.ctrl.op1_sel.asUInt === OP1_RS1 , io.req.bits.rs1_data,
                                                         0.U)
  }

  // operand 2 select
  val op2_data = Mux(uop.ctrl.op2_sel === OP2_IMM,  Sext(imm_xprlen.asUInt, xLen),
                 Mux(uop.ctrl.op2_sel === OP2_IMMC, io.req.bits.uop.prs1(4,0),
                 Mux(uop.ctrl.op2_sel === OP2_RS2 , io.req.bits.rs2_data,
                 Mux(uop.ctrl.op2_sel === OP2_NEXT, Mux(uop.is_rvc, 2.U, 4.U),
                                                    0.U))))

  val alu = Module(new freechips.rocketchip.rocket.ALU())

  alu.io.in1 := op1_data.asUInt
  alu.io.in2 := op2_data.asUInt
  alu.io.fn  := uop.ctrl.op_fcn
  alu.io.dw  := uop.ctrl.fcn_dw

  // Did I just get killed by the previous cycle's branch,
  // or by a flush pipeline?
  val killed = WireInit(false.B)
  when (io.req.bits.kill || IsKilledByBranch(io.brupdate, uop)) {
    killed := true.B
  }

  val rs1 = io.req.bits.rs1_data
  val rs2 = io.req.bits.rs2_data
  val br_eq  = (rs1 === rs2)
  val br_ltu = (rs1.asUInt < rs2.asUInt)
  val br_lt  = (~(rs1(xLen-1) ^ rs2(xLen-1)) & br_ltu |
                rs1(xLen-1) & ~rs2(xLen-1)).asBool

  val pc_sel = MuxLookup(uop.ctrl.br_type, PC_PLUS4)(
                 Seq(   BR_N   -> PC_PLUS4,
                        BR_NE  -> Mux(!br_eq,  PC_BRJMP, PC_PLUS4),
                        BR_EQ  -> Mux( br_eq,  PC_BRJMP, PC_PLUS4),
                        BR_GE  -> Mux(!br_lt,  PC_BRJMP, PC_PLUS4),
                        BR_GEU -> Mux(!br_ltu, PC_BRJMP, PC_PLUS4),
                        BR_LT  -> Mux( br_lt,  PC_BRJMP, PC_PLUS4),
                        BR_LTU -> Mux( br_ltu, PC_BRJMP, PC_PLUS4),
                        BR_J   -> PC_BRJMP,
                        BR_JR  -> PC_JALR
                        ))

  val is_taken = io.req.valid &&
                   !killed &&
                   (uop.is_br || uop.is_jalr || uop.is_jal) &&
                   (pc_sel =/= PC_PLUS4)

  // "mispredict" means that a branch has been resolved and it must be killed
  val mispredict = WireInit(false.B)

  val is_br          = io.req.valid && !killed && uop.is_br && !uop.is_sfb
  val is_jal         = io.req.valid && !killed && uop.is_jal
  val is_jalr        = io.req.valid && !killed && uop.is_jalr

  when (is_br || is_jalr) {
    if (!isJmpUnit) {
      assert (pc_sel =/= PC_JALR)
    }
    when (pc_sel === PC_PLUS4) {
      mispredict := uop.taken
    }
    when (pc_sel === PC_BRJMP) {
      mispredict := !uop.taken
    }
  }

  val brinfo = Wire(new BrResolutionInfo)

  // note: jal doesn't allocate a branch-mask, so don't clear a br-mask bit
  brinfo.valid          := is_br || is_jalr
  brinfo.mispredict     := mispredict
  brinfo.uop            := uop
  brinfo.cfi_type       := Mux(is_jalr, CFI_JALR,
                           Mux(is_br  , CFI_BR, CFI_X))
  brinfo.taken          := is_taken
  brinfo.pc_sel         := pc_sel

  brinfo.jalr_target    := DontCare

  // Branch/Jump Target Calculation
  // For jumps we read the FTQ, and can calculate the target
  // For branches we emit the offset for the core to redirect if necessary
  val target_offset = imm_xprlen(20,0).asSInt
  brinfo.jalr_target := DontCare
  if (isJmpUnit) {
    def encodeVirtualAddress(a0: UInt, ea: UInt) = if (vaddrBitsExtended == vaddrBits) {
      ea
    } else {
      // Efficient means to compress 64-bit VA into vaddrBits+1 bits.
      // (VA is bad if VA(vaddrBits) != VA(vaddrBits-1)).
      val a = a0.asSInt >> vaddrBits
      val msb = Mux(a === 0.S || a === -1.S, ea(vaddrBits), !ea(vaddrBits-1))
      Cat(msb, ea(vaddrBits-1,0))
    }

    val jalr_target_base = io.req.bits.rs1_data.asSInt
    val jalr_target_xlen = Wire(UInt(xLen.W))
    jalr_target_xlen := (jalr_target_base + target_offset).asUInt
    val jalr_target = (encodeVirtualAddress(jalr_target_xlen, jalr_target_xlen).asSInt & -2.S).asUInt

    brinfo.jalr_target := jalr_target
    val cfi_idx = ((uop.pc_lob ^ Mux(io.get_ftq_pc.entry.start_bank === 1.U, 1.U << log2Ceil(bankBytes), 0.U)))(log2Ceil(fetchWidth),1)

    when (pc_sel === PC_JALR) {
      mispredict := !io.get_ftq_pc.next_val ||
                    (io.get_ftq_pc.next_pc =/= jalr_target) ||
                    !io.get_ftq_pc.entry.cfi_idx.valid ||
                    (io.get_ftq_pc.entry.cfi_idx.bits =/= cfi_idx)
    }
  }

  brinfo.target_offset := target_offset

  io.brinfo := brinfo

// Response
// TODO add clock gate on resp bits from functional units
//   io.resp.bits.data := RegEnable(alu.io.out, io.req.valid)
//   val reg_data = Reg(outType = Bits(width = xLen))
//   reg_data := alu.io.out
//   io.resp.bits.data := reg_data

  val r_val  = RegInit(VecInit(Seq.fill(numStages) { false.B }))
  val r_data = Reg(Vec(numStages, UInt(xLen.W)))
  val r_pred = Reg(Vec(numStages, Bool()))
  val alu_out = Mux(io.req.bits.uop.is_sfb_shadow && io.req.bits.pred_data,
    Mux(io.req.bits.uop.ldst_is_rs1, io.req.bits.rs1_data, io.req.bits.rs2_data),
    Mux(io.req.bits.uop.uopc === uopMOV, io.req.bits.rs2_data, alu.io.out))
  r_val (0) := io.req.valid
  r_data(0) := Mux(io.req.bits.uop.is_sfb_br, pc_sel === PC_BRJMP, alu_out)
  r_pred(0) := io.req.bits.uop.is_sfb_shadow && io.req.bits.pred_data
  for (i <- 1 until numStages) {
    r_val(i)  := r_val(i-1)
    r_data(i) := r_data(i-1)
    r_pred(i) := r_pred(i-1)
  }
  io.resp.bits.data := r_data(numStages-1)
  io.resp.bits.predicated := r_pred(numStages-1)
  // Bypass
  // for the ALU, we can bypass same cycle as compute
  require (numStages >= 1)
  require (numBypassStages >= 1)
  io.bypass(0).valid := io.req.valid
  io.bypass(0).bits.data := Mux(io.req.bits.uop.is_sfb_br, pc_sel === PC_BRJMP, alu_out)
  for (i <- 1 until numStages) {
    io.bypass(i).valid := r_val(i-1)
    io.bypass(i).bits.data := r_data(i-1)
  }

  // Exceptions
  io.resp.bits.fflags.valid := false.B
}

/**
 * Functional unit that passes in base+imm to calculate addresses, and passes store data
 * to the LSU.
 * For floating point, 65bit FP store-data needs to be decoded into 64bit FP form
 */
class MemAddrCalcUnit(implicit p: Parameters)
  extends PipelinedFunctionalUnit(
    numStages = 0,
    numBypassStages = 0,
    earliestBypassStage = 0,
    dataWidth = 65, // TODO enable this only if FP is enabled?
    isMemAddrCalcUnit = true)
  with freechips.rocketchip.rocket.constants.MemoryOpConstants
  with freechips.rocketchip.rocket.constants.ScalarOpConstants
{
  // perform address calculation
  val sum = (io.req.bits.rs1_data.asSInt + io.req.bits.uop.imm_packed(19,8).asSInt).asUInt
  val ea_sign = Mux(sum(vaddrBits-1), ~sum(63,vaddrBits) === 0.U,
                                       sum(63,vaddrBits) =/= 0.U)
  val effective_address = Cat(ea_sign, sum(vaddrBits-1,0)).asUInt

  val store_data = io.req.bits.rs2_data

  io.resp.bits.addr := effective_address
  io.resp.bits.data := store_data

  if (dataWidth > 63) {
    assert (!(io.req.valid && io.req.bits.uop.ctrl.is_std &&
      io.resp.bits.data(64).asBool === true.B), "65th bit set in MemAddrCalcUnit.")

    assert (!(io.req.valid && io.req.bits.uop.ctrl.is_std && io.req.bits.uop.fp_val),
      "FP store-data should now be going through a different unit.")
  }

  assert (!(io.req.bits.uop.fp_val && io.req.valid && io.req.bits.uop.uopc =/=
          uopLD && io.req.bits.uop.uopc =/= uopSTA),
          "[maddrcalc] assert we never get store data in here.")

  // Handle misaligned exceptions
  val size = io.req.bits.uop.mem_size
  val misaligned =
    (size === 1.U && (effective_address(0) =/= 0.U)) ||
    (size === 2.U && (effective_address(1,0) =/= 0.U)) ||
    (size === 3.U && (effective_address(2,0) =/= 0.U))

  val bkptu = Module(new BreakpointUnit(nBreakpoints))
  bkptu.io.status   := io.status
  bkptu.io.bp       := io.bp
  bkptu.io.pc       := DontCare
  bkptu.io.ea       := effective_address
  bkptu.io.mcontext := io.mcontext
  bkptu.io.scontext := io.scontext

  val ma_ld  = io.req.valid && io.req.bits.uop.uopc === uopLD && misaligned
  val ma_st  = io.req.valid && (io.req.bits.uop.uopc === uopSTA || io.req.bits.uop.uopc === uopAMO_AG) && misaligned
  val dbg_bp = io.req.valid && ((io.req.bits.uop.uopc === uopLD  && bkptu.io.debug_ld) ||
                                (io.req.bits.uop.uopc === uopSTA && bkptu.io.debug_st))
  val bp     = io.req.valid && ((io.req.bits.uop.uopc === uopLD  && bkptu.io.xcpt_ld) ||
                                (io.req.bits.uop.uopc === uopSTA && bkptu.io.xcpt_st))

  def checkExceptions(x: Seq[(Bool, UInt)]) =
    (x.map(_._1).reduce(_||_), PriorityMux(x))
  val (xcpt_val, xcpt_cause) = checkExceptions(List(
    (ma_ld,  (Causes.misaligned_load).U),
    (ma_st,  (Causes.misaligned_store).U),
    (dbg_bp, (CSR.debugTriggerCause).U),
    (bp,     (Causes.breakpoint).U)))

  io.resp.bits.mxcpt.valid := xcpt_val
  io.resp.bits.mxcpt.bits  := xcpt_cause
  assert (!(ma_ld && ma_st), "Mutually-exclusive exceptions are firing.")

  io.resp.bits.sfence.valid := io.req.valid && io.req.bits.uop.mem_cmd === M_SFENCE
  io.resp.bits.sfence.bits.rs1 := io.req.bits.uop.mem_size(0)
  io.resp.bits.sfence.bits.rs2 := io.req.bits.uop.mem_size(1)
  io.resp.bits.sfence.bits.addr := io.req.bits.rs1_data
  io.resp.bits.sfence.bits.asid := io.req.bits.rs2_data
}

/**
 * Functional unit to wrap lower level FPU
 *
 * Currently, bypassing is unsupported!
 * All FP instructions are padded out to the max latency unit for easy
 * write-port scheduling.
 */
class FPUUnit(implicit p: Parameters)
  extends PipelinedFunctionalUnit(
    numStages = p(tile.TileKey).core.fpu.get.dfmaLatency,
    numBypassStages = 0,
    earliestBypassStage = 0,
    dataWidth = 65,
    needsFcsr = true)
{
  val fpu = Module(new FPU())
  fpu.io.req.valid         := io.req.valid
  fpu.io.req.bits.uop      := io.req.bits.uop
  fpu.io.req.bits.rs1_data := io.req.bits.rs1_data
  fpu.io.req.bits.rs2_data := io.req.bits.rs2_data
  fpu.io.req.bits.rs3_data := io.req.bits.rs3_data
  fpu.io.req.bits.fcsr_rm  := io.fcsr_rm

  io.resp.bits.data              := fpu.io.resp.bits.data
  io.resp.bits.fflags.valid      := fpu.io.resp.bits.fflags.valid
  io.resp.bits.fflags.bits.uop   := io.resp.bits.uop
  io.resp.bits.fflags.bits.flags := fpu.io.resp.bits.fflags.bits.flags // kill me now
}

/**
 * Int to FP conversion functional unit
 *
 * @param latency the amount of stages to delay by
 */
class IntToFPUnit(latency: Int)(implicit p: Parameters)
  extends PipelinedFunctionalUnit(
    numStages = latency,
    numBypassStages = 0,
    earliestBypassStage = 0,
    dataWidth = 65,
    needsFcsr = true)
  with tile.HasFPUParameters
{
  val fp_decoder = Module(new UOPCodeFPUDecoder) // TODO use a simpler decoder
  val io_req = io.req.bits
  fp_decoder.io.uopc := io_req.uop.uopc
  val fp_ctrl = fp_decoder.io.sigs
  val fp_rm = Mux(ImmGenRm(io_req.uop.imm_packed) === 7.U, io.fcsr_rm, ImmGenRm(io_req.uop.imm_packed))
  val req = Wire(new tile.FPInput)
  val tag = fp_ctrl.typeTagIn

  req.viewAsSupertype(new tile.FPUCtrlSigs) := fp_ctrl

  req.rm := fp_rm
  req.in1 := unbox(io_req.rs1_data, tag, None)
  req.in2 := unbox(io_req.rs2_data, tag, None)
  req.in3 := DontCare
  req.typ := ImmGenTyp(io_req.uop.imm_packed)
  req.fmt := DontCare // FIXME: this may not be the right thing to do here
  req.fmaCmd := DontCare

  assert (!(io.req.valid && fp_ctrl.fromint && req.in1(xLen).asBool),
    "[func] IntToFP integer input has 65th high-order bit set!")

  assert (!(io.req.valid && !fp_ctrl.fromint),
    "[func] Only support fromInt micro-ops.")

  val ifpu = Module(new tile.IntToFP(intToFpLatency))
  ifpu.io.in.valid := io.req.valid
  ifpu.io.in.bits := req
  ifpu.io.in.bits.in1 := io_req.rs1_data
  val out_double = Pipe(io.req.valid, fp_ctrl.typeTagOut === D, intToFpLatency).bits

//io.resp.bits.data              := box(ifpu.io.out.bits.data, !io.resp.bits.uop.fp_single)
  io.resp.bits.data              := box(ifpu.io.out.bits.data, out_double)
  io.resp.bits.fflags.valid      := ifpu.io.out.valid
  io.resp.bits.fflags.bits.uop   := io.resp.bits.uop
  io.resp.bits.fflags.bits.flags := ifpu.io.out.bits.exc
}

/**
 * Iterative/unpipelined functional unit, can only hold a single MicroOp at a time
 * assumes at least one register between request and response
 *
 * TODO allow up to N micro-ops simultaneously.
 *
 * @param dataWidth width of the data to be passed into the functional unit
 */
abstract class IterativeFunctionalUnit(dataWidth: Int)(implicit p: Parameters)
  extends FunctionalUnit(
    isPipelined = false,
    numStages = 1,
    numBypassStages = 0,
    dataWidth = dataWidth)
{
  val r_uop = Reg(new MicroOp())

  val do_kill = Wire(Bool())
  do_kill := io.req.bits.kill // irrelevant default

  when (io.req.fire) {
    // update incoming uop
    do_kill := IsKilledByBranch(io.brupdate, io.req.bits.uop) || io.req.bits.kill
    r_uop := io.req.bits.uop
    r_uop.br_mask := GetNewBrMask(io.brupdate, io.req.bits.uop)
  } .otherwise {
    do_kill := IsKilledByBranch(io.brupdate, r_uop) || io.req.bits.kill
    r_uop.br_mask := GetNewBrMask(io.brupdate, r_uop)
  }

  // assumes at least one pipeline register between request and response
  io.resp.bits.uop := r_uop
}

/**
 * Divide functional unit.
 *
 * @param dataWidth data to be passed into the functional unit
 */
class DivUnit(dataWidth: Int)(implicit p: Parameters)
  extends IterativeFunctionalUnit(dataWidth)
{

  // We don't use the iterative multiply functionality here.
  // Instead we use the PipelinedMultiplier
  val div = Module(new freechips.rocketchip.rocket.MulDiv(mulDivParams, width = dataWidth))

  // request
  div.io.req.valid    := io.req.valid && !this.do_kill
  div.io.req.bits.dw  := io.req.bits.uop.ctrl.fcn_dw
  div.io.req.bits.fn  := io.req.bits.uop.ctrl.op_fcn
  div.io.req.bits.in1 := io.req.bits.rs1_data
  div.io.req.bits.in2 := io.req.bits.rs2_data
  div.io.req.bits.tag := DontCare
  io.req.ready        := div.io.req.ready

  // handle pipeline kills and branch misspeculations
  div.io.kill         := this.do_kill

  // response
  io.resp.valid       := div.io.resp.valid && !this.do_kill
  div.io.resp.ready   := io.resp.ready
  io.resp.bits.data   := div.io.resp.bits.data
}

/**
 * Pipelined multiplier functional unit that wraps around the RocketChip pipelined multiplier
 *
 * @param numStages number of pipeline stages
 * @param dataWidth size of the data being passed into the functional unit
 */
class PipelinedMulUnit(numStages: Int, dataWidth: Int)(implicit p: Parameters)
  extends PipelinedFunctionalUnit(
    numStages = numStages,
    numBypassStages = 0,
    earliestBypassStage = 0,
    dataWidth = dataWidth)
{
  val imul = Module(new PipelinedMultiplier(xLen, numStages))
  // request
  imul.io.req.valid    := io.req.valid
  imul.io.req.bits.fn  := io.req.bits.uop.ctrl.op_fcn
  imul.io.req.bits.dw  := io.req.bits.uop.ctrl.fcn_dw
  imul.io.req.bits.in1 := io.req.bits.rs1_data
  imul.io.req.bits.in2 := io.req.bits.rs2_data
  imul.io.req.bits.tag := DontCare
  // response
  io.resp.bits.data    := imul.io.resp.bits.data
}

class BrResolutionInfo(implicit p: Parameters) extends BoomBundle