diff options
Diffstat (limited to 'src/main')
| -rw-r--r-- | src/main/scala/firrtl/EmissionOption.scala | 12 | ||||
| -rw-r--r-- | src/main/scala/firrtl/Emitter.scala | 60 | ||||
| -rw-r--r-- | src/main/scala/firrtl/annotations/MemoryInitAnnotation.scala | 35 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/LowerTypes.scala | 19 |
4 files changed, 111 insertions, 15 deletions
diff --git a/src/main/scala/firrtl/EmissionOption.scala b/src/main/scala/firrtl/EmissionOption.scala index 11c16e2c..91db1f53 100644 --- a/src/main/scala/firrtl/EmissionOption.scala +++ b/src/main/scala/firrtl/EmissionOption.scala @@ -9,6 +9,18 @@ package firrtl */ trait EmissionOption +/** Emission customization options for memories */ +trait MemoryEmissionOption extends EmissionOption { + def initValue: MemoryInitValue = MemoryRandomInit +} + +sealed trait MemoryInitValue +case object MemoryRandomInit extends MemoryInitValue +case class MemoryScalarInit(value: BigInt) extends MemoryInitValue +case class MemoryArrayInit(values: Seq[BigInt]) extends MemoryInitValue + +/** default Emitter behavior for memories */ +case object MemoryEmissionOptionDefault extends MemoryEmissionOption /** Emission customization options for registers */ trait RegisterEmissionOption extends EmissionOption { diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala index 60099137..3b065bf4 100644 --- a/src/main/scala/firrtl/Emitter.scala +++ b/src/main/scala/firrtl/Emitter.scala @@ -475,12 +475,16 @@ class VerilogEmitter extends SeqTransform with Emitter { */ private[firrtl] class EmissionOptions(annotations: AnnotationSeq) { // Private so that we can present an immutable API + private val memoryEmissionOption = new EmissionOptionMap[MemoryEmissionOption](MemoryEmissionOptionDefault) private val registerEmissionOption = new EmissionOptionMap[RegisterEmissionOption](RegisterEmissionOptionDefault) private val wireEmissionOption = new EmissionOptionMap[WireEmissionOption](WireEmissionOptionDefault) private val portEmissionOption = new EmissionOptionMap[PortEmissionOption](PortEmissionOptionDefault) private val nodeEmissionOption = new EmissionOptionMap[NodeEmissionOption](NodeEmissionOptionDefault) private val connectEmissionOption = new EmissionOptionMap[ConnectEmissionOption](ConnectEmissionOptionDefault) + def getMemoryEmissionOption(target: ReferenceTarget): MemoryEmissionOption = + memoryEmissionOption(target) + def getRegisterEmissionOption(target: ReferenceTarget): RegisterEmissionOption = registerEmissionOption(target) @@ -500,6 +504,7 @@ class VerilogEmitter extends SeqTransform with Emitter { case m : SingleTargetAnnotation[ReferenceTarget] @unchecked with EmissionOption => m } // using multiple foreach instead of a single partial function as an Annotation can gather multiple EmissionOptions for simplicity + emissionAnnos.foreach { case a :MemoryEmissionOption => memoryEmissionOption += ((a.target,a)) case _ => } emissionAnnos.foreach { case a :RegisterEmissionOption => registerEmissionOption += ((a.target,a)) case _ => } emissionAnnos.foreach { case a :WireEmissionOption => wireEmissionOption += ((a.target,a)) case _ => } emissionAnnos.foreach { case a :PortEmissionOption => portEmissionOption += ((a.target,a)) case _ => } @@ -586,6 +591,8 @@ class VerilogEmitter extends SeqTransform with Emitter { // reset. To fix this, we need extra initial block logic to reset async reset registers again // post-randomize. val asyncInitials = ArrayBuffer[Seq[Any]]() + // memories need to be initialized even when randomization is disabled + val memoryInitials = ArrayBuffer[Seq[Any]]() val simulates = ArrayBuffer[Seq[Any]]() def bigIntToVLit(bi: BigInt): String = @@ -751,15 +758,45 @@ class VerilogEmitter extends SeqTransform with Emitter { } } - def initialize_mem(s: DefMemory): Unit = { + def initialize_mem(s: DefMemory, opt: MemoryEmissionOption): Unit = { if (s.depth > maxMemSize) { maxMemSize = s.depth } - val index = wref("initvar", s.dataType) - val rstring = rand_string(s.dataType, "RANDOMIZE_MEM_INIT") - ifdefInitials("RANDOMIZE_MEM_INIT") += Seq("for (initvar = 0; initvar < ", bigIntToVLit(s.depth), "; initvar = initvar+1)") - ifdefInitials("RANDOMIZE_MEM_INIT") += Seq(tab, WSubAccess(wref(s.name, s.dataType), index, s.dataType, SinkFlow), - " = ", rstring, ";") + + val dataWidth = bitWidth(s.dataType) + val maxDataValue = (BigInt(1) << dataWidth.toInt) - 1 + + def checkValueRange(value: BigInt, at: String): Unit = { + if(value < 0) throw EmitterException(s"Memory ${at} cannot be initialized with negative value: $value") + if(value > maxDataValue) throw EmitterException(s"Memory ${at} cannot be initialized with value: $value. Too large (> $maxDataValue)!") + } + + opt.initValue match { + case MemoryArrayInit(values) => + if(values.length != s.depth) throw EmitterException( + s"Memory ${s.name} of depth ${s.depth} cannot be initialized with an array of length ${values.length}!" + ) + val memName = LowerTypes.loweredName(wref(s.name, s.dataType)) + values.zipWithIndex.foreach { case (value, addr) => + checkValueRange(value, s"${s.name}[$addr]") + val access = s"$memName[${bigIntToVLit(addr)}]" + memoryInitials += Seq(access, " = ", bigIntToVLit(value), ";") + } + case MemoryScalarInit(value) => + checkValueRange(value, s.name) + // note: s.dataType is the incorrect type for initvar, but it is ignored in the serialization + val index = wref("initvar", s.dataType) + memoryInitials += Seq("for (initvar = 0; initvar < ", bigIntToVLit(s.depth), "; initvar = initvar+1)") + memoryInitials += Seq(tab, WSubAccess(wref(s.name, s.dataType), index, s.dataType, SinkFlow), + " = ", bigIntToVLit(value), ";") + case MemoryRandomInit => + // note: s.dataType is the incorrect type for initvar, but it is ignored in the serialization + val index = wref("initvar", s.dataType) + val rstring = rand_string(s.dataType, "RANDOMIZE_MEM_INIT") + ifdefInitials("RANDOMIZE_MEM_INIT") += Seq("for (initvar = 0; initvar < ", bigIntToVLit(s.depth), "; initvar = initvar+1)") + ifdefInitials("RANDOMIZE_MEM_INIT") += Seq(tab, WSubAccess(wref(s.name, s.dataType), index, s.dataType, SinkFlow), + " = ", rstring, ";") + } } def simulate(clk: Expression, en: Expression, s: Seq[Any], cond: Option[String], info: Info) = { @@ -914,12 +951,13 @@ class VerilogEmitter extends SeqTransform with Emitter { } instdeclares += Seq(");") case sx: DefMemory => + val options = emissionOptions.getMemoryEmissionOption(moduleTarget.ref(sx.name)) val fullSize = sx.depth * (sx.dataType match { case GroundType(IntWidth(width)) => width }) val decl = if (fullSize > (1 << 29)) "reg /* sparse */" else "reg" declareVectorType(decl, sx.name, sx.dataType, sx.depth, sx.info) - initialize_mem(sx) + initialize_mem(sx, options) if (sx.readLatency != 0 || sx.writeLatency != 1) throw EmitterException("All memories should be transformed into " + "blackboxes or combinational by previous passses") @@ -1014,7 +1052,7 @@ class VerilogEmitter extends SeqTransform with Emitter { emit(Seq(tab, "end")) } - if (initials.nonEmpty || ifdefInitials.nonEmpty) { + if (initials.nonEmpty || ifdefInitials.nonEmpty || memoryInitials.nonEmpty) { emit(Seq("// Register and memory initialization")) emit(Seq("`ifdef RANDOMIZE_GARBAGE_ASSIGN")) emit(Seq("`define RANDOMIZE")) @@ -1031,7 +1069,8 @@ class VerilogEmitter extends SeqTransform with Emitter { emit(Seq("`ifndef RANDOM")) emit(Seq("`define RANDOM $random")) emit(Seq("`endif")) - emit(Seq("`ifdef RANDOMIZE_MEM_INIT")) + // the initvar is also used to initialize memories to constants + if(memoryInitials.isEmpty) emit(Seq("`ifdef RANDOMIZE_MEM_INIT")) // Since simulators don't actually support memories larger than 2^31 - 1, there is no reason // to change Verilog emission in the common case. Instead, we only emit a larger initvar // where necessary @@ -1042,7 +1081,7 @@ class VerilogEmitter extends SeqTransform with Emitter { val width = maxMemSize.bitLength - 1 // minus one because [width-1:0] has a width of "width" emit(Seq(s" reg [$width:0] initvar;")) } - emit(Seq("`endif")) + if(memoryInitials.isEmpty) emit(Seq("`endif")) emit(Seq("`ifndef SYNTHESIS")) // User-defined macro of code to run before an initial block emit(Seq("`ifdef FIRRTL_BEFORE_INITIAL")) @@ -1072,6 +1111,7 @@ class VerilogEmitter extends SeqTransform with Emitter { for (x <- initials) emit(Seq(tab, x)) for (x <- asyncInitials) emit(Seq(tab, x)) emit(Seq(" `endif // RANDOMIZE")) + for(x <- memoryInitials) emit(Seq(tab, x)) emit(Seq("end // initial")) // User-defined macro of code to run after an initial block emit(Seq("`ifdef FIRRTL_AFTER_INITIAL")) diff --git a/src/main/scala/firrtl/annotations/MemoryInitAnnotation.scala b/src/main/scala/firrtl/annotations/MemoryInitAnnotation.scala new file mode 100644 index 00000000..44a8e3b5 --- /dev/null +++ b/src/main/scala/firrtl/annotations/MemoryInitAnnotation.scala @@ -0,0 +1,35 @@ +// See LICENSE for license details. + +package firrtl.annotations + +import firrtl.{MemoryArrayInit, MemoryEmissionOption, MemoryInitValue, MemoryRandomInit, MemoryScalarInit} + +/** + * Represents the initial value of the annotated memory. + * While not supported on normal ASIC flows, it can be useful for simulation and FPGA flows. + * This annotation is consumed by the verilog emitter. + */ +sealed trait MemoryInitAnnotation extends SingleTargetAnnotation[ReferenceTarget] with MemoryEmissionOption { + def isRandomInit: Boolean +} + +/** Randomly initialize the `target` memory. This is the same as the default behavior. */ +case class MemoryRandomInitAnnotation(target: ReferenceTarget) extends MemoryInitAnnotation { + override def duplicate(n: ReferenceTarget): Annotation = copy(n) + override def initValue: MemoryInitValue = MemoryRandomInit + override def isRandomInit: Boolean = true +} + +/** Initialize all entries of the `target` memory with the scalar `value`. */ +case class MemoryScalarInitAnnotation(target: ReferenceTarget, value: BigInt) extends MemoryInitAnnotation { + override def duplicate(n: ReferenceTarget): Annotation = copy(n) + override def initValue: MemoryInitValue = MemoryScalarInit(value) + override def isRandomInit: Boolean = false +} + +/** Initialize the `target` memory with the array of `values` which must be the same size as the memory depth. */ +case class MemoryArrayInitAnnotation(target: ReferenceTarget, values: Seq[BigInt]) extends MemoryInitAnnotation { + override def duplicate(n: ReferenceTarget): Annotation = copy(n) + override def initValue: MemoryInitValue = MemoryArrayInit(values) + override def isRandomInit: Boolean = false +}
\ No newline at end of file diff --git a/src/main/scala/firrtl/passes/LowerTypes.scala b/src/main/scala/firrtl/passes/LowerTypes.scala index 4a87ff8b..ccfe5828 100644 --- a/src/main/scala/firrtl/passes/LowerTypes.scala +++ b/src/main/scala/firrtl/passes/LowerTypes.scala @@ -8,6 +8,7 @@ import firrtl.ir._ import firrtl.Utils._ import MemPortUtils.memType import firrtl.Mappers._ +import firrtl.annotations.MemoryInitAnnotation /** Removes all aggregate types from a [[firrtl.ir.Circuit]] * @@ -166,9 +167,9 @@ object LowerTypes extends Transform with DependencyAPIMigration { case e @ (_: UIntLiteral | _: SIntLiteral) => e } def lowerTypesStmt(memDataTypeMap: MemDataTypeMap, - minfo: Info, mname: String, renames: RenameMap)(s: Statement): Statement = { + minfo: Info, mname: String, renames: RenameMap, initializedMems: Set[(String, String)])(s: Statement): Statement = { val info = get_info(s) match {case NoInfo => minfo case x => x} - s map lowerTypesStmt(memDataTypeMap, info, mname, renames) match { + s map lowerTypesStmt(memDataTypeMap, info, mname, renames, initializedMems) match { case s: DefWire => s.tpe match { case _: GroundType => s case _ => @@ -210,6 +211,10 @@ object LowerTypes extends Transform with DependencyAPIMigration { sx.dataType match { case _: GroundType => sx case _ => + // right now only ground type memories can be initialized + if(initializedMems.contains((mname, sx.name))) { + error(s"Cannot initialize memory of non ground type ${sx.dataType.serialize}")(info, mname) + } // Rename ports val seen: mutable.Set[String] = mutable.Set[String]() create_exps(sx.name, memType(sx)) foreach { e => @@ -264,7 +269,7 @@ object LowerTypes extends Transform with DependencyAPIMigration { } } - def lowerTypes(renames: RenameMap)(m: DefModule): DefModule = { + def lowerTypes(renames: RenameMap, initializedMems: Set[(String, String)])(m: DefModule): DefModule = { val memDataTypeMap = new MemDataTypeMap renames.setModule(m.name) // Lower Ports @@ -280,15 +285,19 @@ object LowerTypes extends Transform with DependencyAPIMigration { case m: ExtModule => m copy (ports = portsx) case m: Module => - m copy (ports = portsx) map lowerTypesStmt(memDataTypeMap, m.info, m.name, renames) + m copy (ports = portsx) map lowerTypesStmt(memDataTypeMap, m.info, m.name, renames, initializedMems) } } def execute(state: CircuitState): CircuitState = { + // remember which memories need to be initialized, for these memories, lowering non-ground types is not supported + val initializedMems = state.annotations.collect{ + case m : MemoryInitAnnotation if !m.isRandomInit => + (m.target.encapsulatingModule, m.target.ref) }.toSet val c = state.circuit val renames = RenameMap() renames.setCircuit(c.main) - val result = c copy (modules = c.modules map lowerTypes(renames)) + val result = c copy (modules = c.modules map lowerTypes(renames, initializedMems)) CircuitState(result, outputForm, state.annotations, Some(renames)) } } |
