summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Eduardo2021-03-11 18:25:54 -0300
committerGitHub2021-03-11 21:25:54 +0000
commit9ea57e031f834841609a30031ddab08fe4f47029 (patch)
tree79596d676344408558bf80ce9c2bffe0e59d38e2
parent36e722394cef921967232c63c6e50f5d6bbc0d26 (diff)
Import memory files inline for Verilog generation (#1805)
This annotation adds memory import with inline generation for the emmiter. Supports both readmemh and readmemb statements based on argument.
-rw-r--r--.gitignore3
-rw-r--r--src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala82
-rw-r--r--src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala53
3 files changed, 136 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index 5e727151..5da2d258 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,6 @@ test_run_dir
*~
\#*\#
.\#*
+.metals
+.bloop
+metals.sbt
diff --git a/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala b/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala
index d91d97b7..93981485 100644
--- a/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala
+++ b/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala
@@ -40,7 +40,7 @@ case class ChiselLoadMemoryAnnotation[T <: Data](
}
-/** [[loadMemoryFromFile]] is an annotation generator that helps with loading a memory from a text file. This relies on
+/** [[loadMemoryFromFile]] is an annotation generator that helps with loading a memory from a text file as a bind module. This relies on
* Verilator and Verilog's `\$readmemh` or `\$readmemb`. The [[https://github.com/freechipsproject/treadle Treadle
* backend]] can also recognize this annotation and load memory at run-time.
*
@@ -116,6 +116,86 @@ object loadMemoryFromFile {
}
}
+
+/** [[loadMemoryFromFileInline]] is an annotation generator that helps with loading a memory from a text file inlined in
+ * the Verilog module. This relies on Verilator and Verilog's `\$readmemh` or `\$readmemb`.
+ * The [[https://github.com/freechipsproject/treadle Treadlebackend]] can also recognize this annotation and load memory at run-time.
+ *
+ * This annotation, when the FIRRTL compiler runs, triggers the [[MemoryFileInlineAnnotation]] that will add Verilog
+ * directives inlined to the module enabling the specified memories to be initialized from files.
+ * The module supports both `hex` and `bin` files by passing the appropriate [[MemoryLoadFileType.FileType]] argument with
+ * [[MemoryLoadFileType.Hex]] or [[MemoryLoadFileType.Binary]]. Hex is the default.
+ *
+ * ==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 the file `/workspace/workdir/mem1.hex.txt` just add an import and annotate the memory:
+ * {{{
+ * import chisel3._
+ * import chisel3.util.experimental.loadMemoryFromFileInline // <<-- 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)
+ * loadMemoryFromFileInline(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. The following example shows such a
+ * file formatted to use hex:
+ * {{{
+ * 0
+ * 7
+ * d
+ * 15
+ * }}}
+ *
+ * A binary file can be similarly constructed.
+ * Chisel does not validate the file format or existence. It is supposed to be in a path accessible by the synthesis
+ * tool together with the generated Verilog.
+ *
+ * @see Chisel3 Wiki entry on
+ * [[https://github.com/freechipsproject/chisel3/wiki/Chisel-Memories#loading-memories-in-simulation "Loading Memories
+ * in Simulation"]]
+ */
+object loadMemoryFromFileInline {
+
+
+ /** Annotate a memory such that it can be initialized inline using a file
+ * @param memory the memory
+ * @param fileName the file used for initialization
+ * @param hexOrBinary whether the file uses a hex or binary number representation
+ */
+ def apply[T <: Data](
+ memory: MemBase[T],
+ fileName: String,
+ hexOrBinary: MemoryLoadFileType.FileType = MemoryLoadFileType.Hex
+ ): Unit = {
+ annotate(new ChiselAnnotation {
+ override def toFirrtl = MemoryFileInlineAnnotation(memory.toTarget, fileName, hexOrBinary)
+ })
+ }
+}
+
/** 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
diff --git a/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala b/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala
index 3be649e1..8a998496 100644
--- a/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala
+++ b/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala
@@ -6,7 +6,7 @@ import java.io.File
import chisel3._
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
-import chisel3.util.experimental.loadMemoryFromFile
+import chisel3.util.experimental.{loadMemoryFromFile,loadMemoryFromFileInline}
import chisel3.util.log2Ceil
import firrtl.FirrtlExecutionSuccess
import firrtl.annotations.MemoryLoadFileType
@@ -33,6 +33,26 @@ class UsesThreeMems(memoryDepth: Int, memoryType: Data) extends Module {
io.value3 := memory3(io.address)
}
+class UsesThreeMemsInline(memoryDepth: Int, memoryType: Data, memoryFile: String, hexOrBinary: MemoryLoadFileType.FileType) extends Module {
+ val io = IO(new Bundle {
+ val address = Input(UInt(memoryType.getWidth.W))
+ val value1 = Output(memoryType)
+ val value2 = Output(memoryType)
+ val value3 = Output(memoryType)
+ })
+
+ val memory1 = Mem(memoryDepth, memoryType)
+ val memory2 = Mem(memoryDepth, memoryType)
+ val memory3 = Mem(memoryDepth, memoryType)
+ loadMemoryFromFileInline(memory1, memoryFile, hexOrBinary)
+ loadMemoryFromFileInline(memory2, memoryFile, hexOrBinary)
+ loadMemoryFromFileInline(memory3, memoryFile, hexOrBinary)
+
+ io.value1 := memory1(io.address)
+ io.value2 := memory2(io.address)
+ io.value3 := memory3(io.address)
+}
+
class UsesMem(memoryDepth: Int, memoryType: Data) extends Module {
val io = IO(new Bundle {
val address = Input(UInt(memoryType.getWidth.W))
@@ -205,4 +225,35 @@ class LoadMemoryFromFileSpec extends AnyFreeSpec with Matchers {
file.delete()
}
+ "Module with more than one hex memory inline should work" in {
+ val testDirName = "test_run_dir/load_three_memory_spec_inline"
+
+ val result = (new ChiselStage).execute(
+ args = Array("-X", "verilog", "--target-dir", testDirName),
+ annotations = Seq(ChiselGeneratorAnnotation(() => new UsesThreeMemsInline(memoryDepth = 8, memoryType = UInt(16.W), "./testmem.h", MemoryLoadFileType.Hex)))
+ )
+ val dir = new File(testDirName)
+ val file = new File(dir, s"UsesThreeMemsInline.v")
+ file.exists() should be (true)
+ val fileText = io.Source.fromFile(file).getLines().mkString("\n")
+ fileText should include (s"""$$readmemh("./testmem.h", memory1);""")
+ fileText should include (s"""$$readmemh("./testmem.h", memory2);""")
+ fileText should include (s"""$$readmemh("./testmem.h", memory3);""")
+ }
+
+ "Module with more than one bin memory inline should work" in {
+ val testDirName = "test_run_dir/load_three_memory_spec_inline"
+
+ val result = (new ChiselStage).execute(
+ args = Array("-X", "verilog", "--target-dir", testDirName),
+ annotations = Seq(ChiselGeneratorAnnotation(() => new UsesThreeMemsInline(memoryDepth = 8, memoryType = UInt(16.W), "testmem.bin", MemoryLoadFileType.Binary)))
+ )
+ val dir = new File(testDirName)
+ val file = new File(dir, s"UsesThreeMemsInline.v")
+ file.exists() should be (true)
+ val fileText = io.Source.fromFile(file).getLines().mkString("\n")
+ fileText should include (s"""$$readmemb("testmem.bin", memory1);""")
+ fileText should include (s"""$$readmemb("testmem.bin", memory2);""")
+ fileText should include (s"""$$readmemb("testmem.bin", memory3);""")
+ }
}