summaryrefslogtreecommitdiff
path: root/src/main/scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala')
-rw-r--r--src/main/scala/chisel3/package.scala2
-rw-r--r--src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala234
2 files changed, 236 insertions, 0 deletions
diff --git a/src/main/scala/chisel3/package.scala b/src/main/scala/chisel3/package.scala
index f0542d7e..7f1ad040 100644
--- a/src/main/scala/chisel3/package.scala
+++ b/src/main/scala/chisel3/package.scala
@@ -245,6 +245,8 @@ package object chisel3 { // scalastyle:ignore package.object.name
type BlackBox = chisel3.core.BlackBox
+ type InstanceId = chisel3.internal.InstanceId
+
val Mem = chisel3.core.Mem
type MemBase[T <: Data] = chisel3.core.MemBase[T]
type Mem[T <: Data] = chisel3.core.Mem[T]
diff --git a/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala b/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala
new file mode 100644
index 00000000..04a01fe9
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala
@@ -0,0 +1,234 @@
+// See LICENSE for license details.
+
+package chisel3.util.experimental
+
+import chisel3._
+import chisel3.experimental.annotate
+// import chisel3.InstanceId
+import chisel3.experimental.{ChiselAnnotation, RunFirrtlTransform}
+import firrtl.annotations.{MemoryLoadFileType, _}
+import firrtl.ir.{Module => _, _}
+import firrtl.transforms.BlackBoxInlineAnno
+import firrtl.Mappers._
+import firrtl.{AnnotationSeq, CircuitForm, CircuitState, EmitCircuitAnnotation, LowForm, Transform, VerilogEmitter}
+
+import scala.collection.mutable
+
+/** This is the annotation created when using [[loadMemoryFromFile]], it records the memory, the load file
+ * and the format of the file.
+ * @param target memory to load
+ * @param fileName name of input file
+ * @param hexOrBinary use $readmemh or $readmemb, i.e. hex or binary text input, default is hex
+ */
+case class ChiselLoadMemoryAnnotation[T <: Data](
+ target: MemBase[T],
+ fileName: String,
+ hexOrBinary: MemoryLoadFileType.FileType = MemoryLoadFileType.Hex
+)
+ extends ChiselAnnotation with RunFirrtlTransform {
+
+ if(fileName.isEmpty) {
+ throw new Exception(
+ s"""LoadMemory from file annotations file empty file name"""
+ )
+ }
+
+ def transformClass: Class[LoadMemoryTransform] = classOf[LoadMemoryTransform]
+
+ def toFirrtl: LoadMemoryAnnotation = {
+ LoadMemoryAnnotation(target.toNamed.asInstanceOf[ComponentName], fileName, hexOrBinary)
+ }
+}
+
+
+object loadMemoryFromFile {
+ /** Use this annotation generator to load a memory from a text file by using verilator and
+ * verilog's $readmemh or $readmemb.
+ * The treadle backend can also recognize this annotation and load memory at run-time.
+ *
+ * This annotation triggers the [[LoadMemoryTransform]] which will take add the verilog directive to
+ * the relevant module by using the creating separate modules that are bound to the modules containing
+ * the memories to be loaded.
+ *
+ * ==Example module==
+ *
+ * Consider a simple Module containing a memory
+ * {{{
+ * import chisel3._
+ * class UsesMem(memoryDepth: Int, memoryType: Data) extends Module {
+ * val io = IO(new Bundle {
+ * val address = Input(UInt(memoryType.getWidth.W))
+ * val value = Output(memoryType)
+ * })
+ * val memory = Mem(memoryDepth, memoryType)
+ * io.value := memory(io.address)
+ * }
+ * }}}
+ *
+ * ==Above module with annotation==
+ *
+ * To load this memory from a file /workspace/workdir/mem1.hex.txt
+ * Just add an import and annotate the memory
+ * {{{
+ * import chisel3._
+ * import chisel3.util.experimental.loadMemoryFromFile // <<-- new import here
+ * class UsesMem(memoryDepth: Int, memoryType: Data) extends Module {
+ * val io = IO(new Bundle {
+ * val address = Input(UInt(memoryType.getWidth.W))
+ * val value = Output(memoryType)
+ * })
+ * val memory = Mem(memoryDepth, memoryType)
+ * io.value := memory(io.address)
+ * loadMemoryFromFile(memory, "/workspace/workdir/mem1.hex.txt") // <<-- Note the annotation here
+ * }
+ * }}}
+ *
+ * ==Example file format==
+ * A memory file should consist of ascii text in either hex or binary format
+ * Example (a file containing the decimal values 0, 7, 14, 21):
+ * {{{
+ * 0
+ * 7
+ * d
+ * 15
+ * }}}
+ * Binary file is similarly constructed.
+ *
+ * ==More info==
+ * See the LoadMemoryFromFileSpec.scala in the test suite for more examples
+ * @see <a href="https://github.com/freechipsproject/chisel3/wiki/Chisel-Memories">Load Memories in the wiki</a>
+ */
+ def apply[T <: Data](
+ memory: MemBase[T],
+ fileName: String,
+ hexOrBinary: MemoryLoadFileType.FileType = MemoryLoadFileType.Hex
+ ): Unit = {
+ annotate(ChiselLoadMemoryAnnotation(memory, fileName))
+ }
+}
+
+/**
+ * This transform only is activated if verilog is being generated
+ * (determined by presence of the proper emit annotation)
+ * when activated it creates additional verilog files that contain
+ * modules bound to the modules that contain an initializable memory
+ *
+ * Currently the only non-verilog based simulation that can support loading
+ * memory from a file is treadle but it does not need this transform
+ * to do that.
+ */
+//scalastyle:off method.length
+class LoadMemoryTransform extends Transform {
+ def inputForm: CircuitForm = LowForm
+ def outputForm: CircuitForm = LowForm
+
+ private var memoryCounter: Int = -1
+
+ private val bindModules: mutable.ArrayBuffer[BlackBoxInlineAnno] = new mutable.ArrayBuffer()
+
+ private val verilogEmitter: VerilogEmitter = new VerilogEmitter
+
+ /**
+ * run the pass
+ * @param circuit the circuit
+ * @param annotations all the annotations
+ * @return
+ */
+ def run(circuit: Circuit, annotations: AnnotationSeq): Circuit = {
+ val groups = annotations
+ .collect{ case m: LoadMemoryAnnotation => m }
+ .groupBy(_.target.serialize)
+ val memoryAnnotations = groups.map { case (key, annos) =>
+ if (annos.size > 1) {
+ throw new Exception(
+ s"Multiple (${annos.size} found for memory $key one LoadMemoryAnnotation is allowed per memory"
+ )
+ }
+ key -> annos.head
+ }
+
+ val modulesByName = circuit.modules.collect { case module: firrtl.ir.Module => module.name -> module }.toMap
+
+ /**
+ * walk the module and for memories that have LoadMemory annotations
+ * generate the bindable modules for verilog emission
+ *
+ * @param myModule module being searched for memories
+ */
+ def processModule(myModule: DefModule): DefModule = {
+
+ def makePath(componentName: String): String = {
+ circuit.main + "." + myModule.name + "." + componentName
+ }
+
+ def processMemory(name: String): Unit = {
+ val fullMemoryName = makePath(s"$name")
+
+ memoryAnnotations.get(fullMemoryName) match {
+ case Some(lma @ LoadMemoryAnnotation(ComponentName(componentName, moduleName), _, hexOrBinary, _)) =>
+ val writer = new java.io.StringWriter
+
+ modulesByName.get(moduleName.name).foreach { module =>
+ val renderer = verilogEmitter.getRenderer(module, modulesByName)(writer)
+ val loadFileName = lma.getFileName
+
+ memoryCounter += 1
+ val bindsToName = s"BindsTo_${memoryCounter}_${moduleName.name}"
+ renderer.emitVerilogBind(bindsToName,
+ s"""
+ |initial begin
+ | $$readmem$hexOrBinary("$loadFileName", ${myModule.name}.$componentName);
+ |end
+ """.stripMargin)
+ val inLineText = writer.toString + "\n" +
+ s"""bind ${myModule.name} $bindsToName ${bindsToName}_Inst(.*);"""
+
+ val blackBoxInline = BlackBoxInlineAnno(
+ moduleName,
+ moduleName.serialize + "." + componentName + ".v",
+ inLineText
+ )
+
+ bindModules += blackBoxInline
+ }
+
+ case _ =>
+ }
+ }
+
+ def processStatements(statement: Statement): Statement = {
+ statement match {
+ case m: DefMemory => processMemory(m.name)
+ case s => s map processStatements
+ }
+ statement
+ }
+
+ myModule match {
+ case module: firrtl.ir.Module =>
+ processStatements(module.body)
+ case _ =>
+ }
+
+ myModule
+ }
+
+ circuit map processModule
+ }
+
+ def execute(state: CircuitState): CircuitState = {
+ val isVerilog = state.annotations.exists {
+ case EmitCircuitAnnotation(emitter) =>
+ emitter == classOf[VerilogEmitter]
+ case _ =>
+ false
+ }
+ if(isVerilog) {
+ run(state.circuit, state.annotations)
+ state.copy(annotations = state.annotations ++ bindModules)
+ }
+ else {
+ state
+ }
+ }
+}