diff options
Diffstat (limited to 'src/main/scala')
| -rw-r--r-- | src/main/scala/chisel3/package.scala | 2 | ||||
| -rw-r--r-- | src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala | 234 |
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 + } + } +} |
