// SPDX-License-Identifier: Apache-2.0

package firrtl.stage

import firrtl.transforms._
import firrtl.passes.memlib._
import firrtl.options.{HasShellOptions, ShellOption}
import firrtl.annotations.MemorySynthInit

/**
  * This flag enables a set of options that guide the FIRRTL compilation flow to ultimately generate Verilog that is
  * more amenable to using for synthesized FPGA designs. Currently, this flag affects only memories, as the need to emit
  * memories that support downstream inference of hardened RAM macros. These options are not intended to be specialized
  * to any particular vendor; instead, they aim to emit simple Verilog that more closely reflects traditional
  * human-written definitions of synchronous-read memories.
  *
  * 1) Enable the [[firrtl.passes.memlib.InferReadWrite]] transform to reduce port count, where applicable.
  *
  * 2) Use the [[firrtl.transforms.SimplifyMems]] transform to Lower aggregate-typed memories with always-high masks to
  *    packed memories without splitting them into multiple independent ground-typed memories.
  *
  * 3) Use the [[firrtl.passes.memlib.SeparateWriteClocks]] transform to ensure that each write port of a
  *    multiple-write, synchronous-read memory with 'undefined' collision behavior ultimately maps to a separate clocked
  *    process in the emitted Verilog. This avoids the issue of implicitly constraining cross-port collision and write
  *    ordering behavior and helps simplify inference of true dual-port RAM macros.
  *
  * 4) Use the [[firrtl.passes.memlib.SetDefaultReadUnderWrite]] to specify that memories with undefined
  *    read-under-write behavior should map to emitted microarchitectures characteristic of "read-first" ports by
  *    default. This eliminates the difficulty of inferring a RAM macro that matches the strict semantics of
  *    "write-first" ports.
  *
  * 5) Add a [[firrtl.passes.memlib.PassthroughSimpleSyncReadMemsAnnotation]] to allow some synchronous-read memories
  *    and readwrite ports to pass through [[firrtl.passes.memlib.VerilogMemDelays]] without introducing explicit
  *    pipeline registers or splitting ports.
  *
  * 6) Add a [[firrtl.annotations.MemorySynthInit]] to enable memory initialization values to be synthesized.
  */
object OptimizeForFPGA extends HasShellOptions {
  private val fpgaAnnos = Seq(
    InferReadWriteAnnotation,
    RunFirrtlTransformAnnotation(new InferReadWrite),
    RunFirrtlTransformAnnotation(new SeparateWriteClocks),
    DefaultReadFirstAnnotation,
    RunFirrtlTransformAnnotation(new SetDefaultReadUnderWrite),
    RunFirrtlTransformAnnotation(new SimplifyMems),
    PassthroughSimpleSyncReadMemsAnnotation,
    MemorySynthInit
  )
  val options = Seq(
    new ShellOption[Unit](
      longOption = "target:fpga",
      toAnnotationSeq = a => fpgaAnnos,
      helpText = "Choose compilation strategies that generally favor FPGA targets"
    )
  )
}
