diff options
| author | Carlos Eduardo | 2021-04-07 16:01:51 -0300 |
|---|---|---|
| committer | GitHub | 2021-04-07 19:01:51 +0000 |
| commit | e0da5ae47f3674bdd2018a672028290c927274e1 (patch) | |
| tree | f58135f5b2b97a77c8ba0e59f420c20d0b85e978 /docs/src/appendix | |
| parent | d3e2b346d4edfb3f380e4d8dbfc96b540791275e (diff) | |
Add documentation guide about memory initialization (#1850)
* Add documentation guide about memory initialization
* Move information to experimental and add ref
Diffstat (limited to 'docs/src/appendix')
| -rw-r--r-- | docs/src/appendix/experimental-features.md | 150 |
1 files changed, 122 insertions, 28 deletions
diff --git a/docs/src/appendix/experimental-features.md b/docs/src/appendix/experimental-features.md index c989ec97..363f7a06 100644 --- a/docs/src/appendix/experimental-features.md +++ b/docs/src/appendix/experimental-features.md @@ -10,7 +10,7 @@ Chisel has a number of new features that are worth checking out. This page is a - [Module Variants](#module-variants) - [Module Variants](#bundle-literals) - [Interval Type](#interval-type) -- [Loading Memories in Simulation](#loading-memories) +- [Loading Memories for simulation or FPGA initialization](#loading-memories) ### FixedPoint <a name="fixed-point"></a> FixedPoint numbers are basic *Data* type along side of UInt, SInt, etc. Most common math and logic operations @@ -143,49 +143,142 @@ Consider a Interval with a binary point of 3: aaa.bbb | shiftLeftBinaryPoint(2) | a.aabbb | 5 | X | X | increase the precision | | shiftRighBinaryPoint(2) | aaaa.b | 1 | X | X | reduce the precision | -## Loading Memories in simulation <a name="loading-memories"></a> +## Loading Memories for simulation or FPGA initialization <a name="loading-memories"></a> -Chisel now supports an experimental method for annotating memories to be loaded from a text file containing -hex or binary numbers. When using verilog simulation it uses the `$readmemh` or `$readmemb` -verilog extension. The treadle simulator can also load memories using the same annotation. +Chisel supports multiple experimental methods for annotating memories to be loaded from a text file containing hex or binary data. When using verilog simulation it uses the `$readmemh` or `$readmemb` verilog extension. The treadle simulator can also load memories using the same annotation. -### How to annotate -Assuming you have a memory in a Module -```scala mdoc:invisible +### Inline initialization with external file + +Memories can be initialized by generating inline `readmemh` or `readmemb` statements in the output Verilog. + +The function `loadMemoryFromFileInline` from `chisel3.util.experimental` allows the memory to be initialized by the synthesis software from the specified file. Chisel does not validate the file contents nor its location. Both the memory initialization file and the Verilog source should be accessible for the toolchain. + +```scala mdoc:silent import chisel3._ -val memoryDepth = 8 -val memoryType = Bool() -``` -```scala -val memory = Mem(memoryDepth, memoryType) +import chisel3.util.experimental.loadMemoryFromFileInline + +class InitMemInline(memoryFile: String = "") extends Module { + val width: Int = 32 + val io = IO(new Bundle { + val enable = Input(Bool()) + val write = Input(Bool()) + val addr = Input(UInt(10.W)) + val dataIn = Input(UInt(width.W)) + val dataOut = Output(UInt(width.W)) + }) + + val mem = SyncReadMem(1024, UInt(width.W)) + // Initialize memory + if (memoryFile.trim().nonEmpty) { + loadMemoryFromFileInline(mem, memoryFile) + } + io.dataOut := DontCare + when(io.enable) { + val rdwrPort = mem(io.addr) + when (io.write) { rdwrPort := io.dataIn } + .otherwise { io.dataOut := rdwrPort } + } +} ``` -At the top of your file just add the import +The default is to use `$readmemh` (which assumes all numbers in the file are in ascii hex), +but to use ascii binary there is an optional `hexOrBinary` argument which can be set to `MemoryLoadFileType.Hex` or `MemoryLoadFileType.Binary`. You will need to add an additional import. + +By default, the inline initialization will generate the memory `readmem` statements inside an `ifndef SYNTHESIS` block, which suits ASIC workflow. + +Some synthesis tools (like Synplify and Yosys) define `SYNTHESIS` so the `readmem` statement is not read when inside this block. + +To control this, one can use the `MemoryNoSynthInit` and `MemorySynthInit` annotations from `firrtl.annotations`. The former which is the default setting when no annotation is present generates `readmem` inside the block. Using the latter, the statement are generated outside the `ifndef` block so it can be used by FPGA synthesis tools. + +Below an example for initialization suited for FPGA workflows: + ```scala mdoc:silent -import chisel3.util.experimental.loadMemoryFromFile -``` -Now just add the memory annotation using -```scala - loadMemoryFromFile(memory, "/workspace/workdir/mem1.txt") +import chisel3._ +import chisel3.util.experimental.loadMemoryFromFileInline +import chisel3.experimental.{annotate, ChiselAnnotation} +import firrtl.annotations.MemorySynthInit + +class InitMemInlineFPGA(memoryFile: String = "") extends Module { + val width: Int = 32 + val io = IO(new Bundle { + val enable = Input(Bool()) + val write = Input(Bool()) + val addr = Input(UInt(10.W)) + val dataIn = Input(UInt(width.W)) + val dataOut = Output(UInt(width.W)) + }) + + // Notice the annotation below + annotate(new ChiselAnnotation { + override def toFirrtl = + MemorySynthInit + }) + + val mem = SyncReadMem(1024, UInt(width.W)) + if (memoryFile.trim().nonEmpty) { + loadMemoryFromFileInline(mem, memoryFile) + } + io.dataOut := DontCare + when(io.enable) { + val rdwrPort = mem(io.addr) + when (io.write) { rdwrPort := io.dataIn } + .otherwise { io.dataOut := rdwrPort } + } +} ``` -The default is to use `$readmemh` (which assumes all numbers in the file are in ascii hex), -bu to use ascii binary there is an optional third argument. You will need to add an additional import. + +#### SystemVerilog Bind Initialization + +Chisel can also initialize memories by generating a SV bind module with `readmemh` or `readmemb` statements by using the function `loadMemoryFromFile` from `chisel3.util.experimental`. + ```scala mdoc:silent -import firrtl.annotations.MemoryLoadFileType +import chisel3._ +import chisel3.util.experimental.loadMemoryFromFile + +class InitMemBind(val bits: Int, val size: Int, filename: String) extends Module { + val io = IO(new Bundle { + val nia = Input(UInt(bits.W)) + val insn = Output(UInt(32.W)) + }) + + val memory = Mem(size, UInt(32.W)) + io.insn := memory(io.nia >> 2); + loadMemoryFromFile(memory, filename) +} ``` -```scala - loadMemoryFromFile(memory, "/workspace/workdir/mem1.txt", MemoryLoadFileType.Binary) + +Which generates the bind module: + +```verilog +module BindsTo_0_Foo( + input clock, + input reset, + input [31:0] io_nia, + output [31:0] io_insn +); + +initial begin + $readmemh("test.hex", Foo.memory); +end +endmodule + +bind Foo BindsTo_0_Foo BindsTo_0_Foo_Inst(.*); ``` -See: [ComplexMemoryLoadingSpec.scala](https://freechipsproject/chisel-testers/src/test/scala/examples/ComplexMemoryLoadingSpec.scala) and -[LoadMemoryFromFileSpec.scala](https://github.com/freechipsproject/chisel-testers/src/test/scala/examples/LoadMemoryFromFileSpec.scala) -for working examples. ### Notes on files -There is no simple answer to where to put this file. It's probably best to create a resource directory somewhere and reference that through a full path. Because these files may be large, we did not want to copy them. + +There is no simple answer to where to put the `hex` or `bin` file with the initial contents. It's probably best to create a resource directory somewhere and reference that through a full path or place the file beside the generated Verilog. Another option is adding the path to the memory file in the synthesis tool path. Because these files may be large, Chisel does not copy them. > Don't forget there is no decimal option, so a 10 in an input file will be 16 decimal +See: [ComplexMemoryLoadingSpec.scala](https://github.com/freechipsproject/chisel-testers/blob/master/src/test/scala/examples/ComplexMemoryLoadingSpec.scala) and +[LoadMemoryFromFileSpec.scala](https://github.com/freechipsproject/chisel-testers/blob/master/src/test/scala/examples/LoadMemoryFromFileSpec.scala) +for working examples. + + ### Aggregate memories + Aggregate memories are supported but in bit of a clunky way. Since they will be split up into a memory per field, the following convention was adopted. When specifying the file for such a memory the file name should be regarded as a template. If the memory is a Bundle e.g. + ```scala mdoc:compile-only class MemDataType extends Bundle { val a = UInt(16.W) @@ -193,6 +286,7 @@ class MemDataType extends Bundle { val c = Bool() } ``` + The memory will be split into `memory_a`, `memory_b`, and `memory_c`. Similarly if a load file is specified as `"memory-load.txt"` the simulation will expect that there will be three files, `"memory-load_a.txt"`, `"memory-load_b.txt"`, `"memory-load_c.txt"` > Note: The use of `_` and that the memory field name is added before any file suffix. The suffix is optional but if present is considered to be the text after the last `.` in the file name. |
