// SPDX-License-Identifier: Apache-2.0 package firrtl.passes package memlib import firrtl._ import firrtl.ir._ import firrtl.Mappers._ import firrtl.annotations._ import firrtl.stage.Forms /** A component, e.g. register etc. Must be declared only once under the TopAnnotation */ case class NoDedupMemAnnotation(target: ComponentName) extends SingleTargetAnnotation[ComponentName] { def duplicate(n: ComponentName) = NoDedupMemAnnotation(n) } /** Resolves annotation ref to memories that exactly match (except name) another memory */ class ResolveMemoryReference extends Transform with DependencyAPIMigration { override def prerequisites = Forms.MidForm override def optionalPrerequisites = Seq.empty override def optionalPrerequisiteOf = Forms.MidEmitters override def invalidates(a: Transform) = false /** Helper class for determining when two memories are equivalent while igoring * irrelevant details like name and info */ private class WrappedDefAnnoMemory(val underlying: DefAnnotatedMemory) { // Remove irrelevant details for comparison private def generic = underlying.copy(info = NoInfo, name = "", memRef = None) override def hashCode: Int = generic.hashCode override def equals(that: Any): Boolean = that match { case mem: WrappedDefAnnoMemory => this.generic == mem.generic case _ => false } } private def wrap(mem: DefAnnotatedMemory) = new WrappedDefAnnoMemory(mem) // Values are Tuple of Module Name and Memory Instance Name private type AnnotatedMemories = collection.mutable.HashMap[WrappedDefAnnoMemory, (String, String)] private def dedupable(noDedups: Map[String, Set[String]], module: String, memory: String): Boolean = noDedups.get(module).map(!_.contains(memory)).getOrElse(true) /** If a candidate memory is identical except for name to another, add an * annotation that references the name of the other memory. */ def updateMemStmts( mname: String, existingMems: AnnotatedMemories, noDedupMap: Map[String, Set[String]] )(s: Statement ): Statement = s match { // If not dedupable, no need to add to existing (since nothing can dedup with it) // We just return the DefAnnotatedMemory as is in the default case below case m: DefAnnotatedMemory if dedupable(noDedupMap, mname, m.name) => val wrapped = wrap(m) existingMems.get(wrapped) match { case proto @ Some(_) => m.copy(memRef = proto) case None => existingMems(wrapped) = (mname, m.name) m } case s => s.map(updateMemStmts(mname, existingMems, noDedupMap)) } def run(c: Circuit, noDedupMap: Map[String, Set[String]]) = { val existingMems = new AnnotatedMemories val modulesx = c.modules.map(m => m.map(updateMemStmts(m.name, existingMems, noDedupMap))) c.copy(modules = modulesx) } def execute(state: CircuitState): CircuitState = { val (remainingAnnotations, noDedupMemAnnos) = state.annotations.partition { case _: NoDedupMemAnnotation => false case _ => true } val noDedups = noDedupMemAnnos.map { case NoDedupMemAnnotation(ComponentName(cn, ModuleName(mn, _))) => mn -> cn } val noDedupMap: Map[String, Set[String]] = noDedups.groupBy(_._1).mapValues(_.map(_._2).toSet).toMap state.copy( circuit = run(state.circuit, noDedupMap), annotations = remainingAnnotations ) } }