-
Notifications
You must be signed in to change notification settings - Fork 588
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
loadMemoryFromFile() should place readmemh() inline for better compatibility #1293
Comments
Same here while trying to make a RAM module: class SinglePortRAM(sizeKB: Int = 1, width: Int = 32, memoryFile: String = "") extends Module {
val addrWidth = chiselTypeOf((sizeKB * 1024).U)
val io = IO(new Bundle {
val addr1 = Input(addrWidth)
val dataIn1 = Input(UInt(width.W))
val en1 = Input(Bool())
val we1 = Input(Bool())
val dataOut1 = Output(UInt(width.W))
})
println(s"Single-port Memory Parameters:")
println(s" Size: $sizeKB KB")
println(s" Width: $width bit")
println(s" Addr Width: " + io.addr1.getWidth + " bit")
val mem = SyncReadMem(sizeKB * 1024, UInt(width.W))
if (memoryFile.trim().nonEmpty) {
loadMemoryFromFile(mem, memoryFile)
}
when (io.we1) {
mem.write(io.addr1, io.dataIn1)
}
io.dataOut1 := mem.read(io.addr1, io.en1)
} If memfile is provided, it gets written to a separate file that is not synthesizable. |
We have a For examples on how to use it, see:
Would that work for you? |
Hey Kevin, thanks for your response. I understand I cannot pass the .hex file I have to be loaded at synthesis by using the annotation, right since it only supports BigInts? What's the method do do that? |
Yeah, so the method that I suggested assumes that you have the data in Scala. You could of course try and write a function that opens a
There is no separate file involved. You just provide the memory init content in your Chisel generator and the Verilog we produce will contain an |
As someone new to HDLs, I found these methods way more complex than just |
@carlosedp, could you try to see if the following would address your problem? import chisel3.experimental.{ChiselAnnotation, annotate}
import firrtl.annotations.{MemoryArrayInitAnnotation, MemoryLoadFileType}
import scala.io.Source
object loadMemoryFromFileInline {
def apply(mem: MemBase[_], filename: String, hexOrBinary: MemoryLoadFileType.FileType = MemoryLoadFileType.Hex): Unit = {
val radix = hexOrBinary match {
case MemoryLoadFileType.Hex => 16
case MemoryLoadFileType.Binary => 2
}
val src = Source.fromFile(filename)
val lines = src.getLines().toList
src.close()
val data = lines.map(_.trim).filter(_.nonEmpty).map(BigInt(_, radix))
val missingWords = mem.length - data.length
require(missingWords >= 0, s"$filename contained ${data.length} items, but memory only fits ${mem.length}")
val padding = Seq.tabulate(missingWords.toInt)(_ => BigInt(0))
annotate(new ChiselAnnotation {
override def toFirrtl = MemoryArrayInitAnnotation(mem.toTarget, data ++ padding)
})
}
} You should be able to just replace I know that this isn't 100% what you want, but it would be fairly straight forward to add this helper function from your code base and might be a good stop-gap measure until we come up with a more permanent solution. |
Thanks @ekiwi ... just a heads-up is that since the memory gets initialized by directly setting the memory array in Verilog so a 1k Scala module with an 11k .hex became a 282k verilog...
|
Thanks for trying it out. I definitely didn't consider that this might blow up significantly in size (20x .. wow). |
It's instantiated into a 16KB ram that would be pretty much empty.. but still quite a grow. |
@antonblanchard check out chipsalliance/firrtl#2107. I've added a new annotation allowing inline readmem in Verilog emiter. Your sample code would become: import chisel3._
import chisel3.experimental.{ChiselAnnotation, annotate}
import firrtl.annotations.{MemoryFileInlineAnnotation}
class Foo(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);
annotate(new ChiselAnnotation {
override def toFirrtl = MemoryFileInlineAnnotation(memory.toTarget, filename)
})
}
object FooObj extends App {
chisel3.Driver.execute(Array[String](), () => new Foo(32, 1024, "insns.hex"))
} that would generate the appropriate: initial begin
$readmemh("insns.hex", memory);
end It would allow also configuration for binary files instead of hex, like: import firrtl.annotations.{MemoryFileInlineAnnotation, MemoryLoadFileType}
...
annotate(new ChiselAnnotation {
override def toFirrtl = MemoryFileInlineAnnotation(memory.toTarget, "filename.bin", MemoryLoadFileType.Binary)
})
... and output initial begin
$readmemb("filename.bin", memory);
end |
@carlosedp That looks great! Nice work |
Now there is also a method in chisel for this: import chisel3.util.experimental.loadMemoryFromFileInline
...
annotate(new ChiselAnnotation {
override def toFirrtl =
MemorySynthInit
})
val mem = SyncReadMem(1024, UInt(width.W))
loadMemoryFromFileInline(mem, memoryFile) I believe this could be closed now. Any objections? |
There is still the question of whether we should move this API out of experimental and maybe make the inline version the default. |
Ah yes, that would be great! |
Any news on this? Should we move on and close or promote to non-experimental API? |
Would you mind making a PR that makes Then we can discuss the issue on the PR. |
Sure, do I keep the previous behavior of Also should it be in |
Opened #2260 to address this. |
loadMemoryFromFile()
creates a separate file and uses a SV bind statement. A simple example:Yosys doesn't like this, and likely there are other tools that don't either. Is there any reason we don't just place it inline?
The text was updated successfully, but these errors were encountered: