aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/scala/firrtl/EmissionOption.scala12
-rw-r--r--src/main/scala/firrtl/Emitter.scala60
-rw-r--r--src/main/scala/firrtl/annotations/MemoryInitAnnotation.scala35
-rw-r--r--src/main/scala/firrtl/passes/LowerTypes.scala19
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))
}
}