// SPDX-License-Identifier: Apache-2.0 package chiselTests import java.io.File import chisel3._ import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} import chisel3.util.experimental.{loadMemoryFromFile,loadMemoryFromFileInline} import chisel3.util.log2Ceil import firrtl.FirrtlExecutionSuccess import firrtl.annotations.MemoryLoadFileType import org.scalatest.freespec.AnyFreeSpec import org.scalatest.matchers.should.Matchers class UsesThreeMems(memoryDepth: Int, memoryType: Data) 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) loadMemoryFromFile(memory1, "./mem1") loadMemoryFromFile(memory2, "./mem1") loadMemoryFromFile(memory3, "./mem1") io.value1 := memory1(io.address) io.value2 := memory2(io.address) 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)) val value = Output(memoryType) val value1 = Output(memoryType) val value2 = Output(memoryType) }) val memory = Mem(memoryDepth, memoryType) loadMemoryFromFile(memory, "./mem1") io.value := memory(io.address) val low1 = Module(new UsesMemLow(memoryDepth, memoryType)) val low2 = Module(new UsesMemLow(memoryDepth, memoryType)) low2.io.address := io.address low1.io.address := io.address io.value1 := low1.io.value io.value2 := low2.io.value } class UsesMemLow(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) loadMemoryFromFile(memory, "./mem2") io.value := memory(io.address) } class FileHasSuffix(memoryDepth: Int, memoryType: Data) extends Module { val io = IO(new Bundle { val address = Input(UInt(memoryType.getWidth.W)) val value = Output(memoryType) val value2 = Output(memoryType) }) val memory = Mem(memoryDepth, memoryType) loadMemoryFromFile(memory, "./mem1.txt") io.value := memory(io.address) val low = Module(new UsesMemLow(memoryDepth, memoryType)) low.io.address := io.address io.value2 := low.io.value } class MemoryShape extends Bundle { val a = UInt(8.W) val b = SInt(8.W) val c = Bool() } class HasComplexMemory(memoryDepth: Int) extends Module { val io = IO(new Bundle { val address = Input(UInt(log2Ceil(memoryDepth).W)) val value = Output(new MemoryShape) }) val memory = Mem(memoryDepth, new MemoryShape) loadMemoryFromFile(memory, "./mem", MemoryLoadFileType.Hex) io.value := memory(io.address) } class HasBinarySupport(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) loadMemoryFromFile(memory, "./mem", MemoryLoadFileType.Binary) io.value := memory(io.address) } /** * The following tests are a bit incomplete and check that the output verilog is properly constructed * For more complete working examples * @see Chisel Testers LoadMemoryFromFileSpec.scala */ class LoadMemoryFromFileSpec extends AnyFreeSpec with Matchers { def fileExistsWithMem(file: File, mem: Option[String] = None): Unit = { info(s"$file exists") file.exists() should be (true) mem.foreach( m => { info(s"Memory $m is referenced in $file") val found = io.Source.fromFile(file).getLines.exists { _.contains(s"""readmemh("$m"""") } found should be (true) } ) file.delete() } "Users can specify a source file to load memory from" in { val testDirName = "test_run_dir/load_memory_spec" val result = (new ChiselStage).execute( args = Array("-X", "verilog", "--target-dir", testDirName), annotations = Seq(ChiselGeneratorAnnotation(() => new UsesMem(memoryDepth = 8, memoryType = UInt(16.W)))) ) val dir = new File(testDirName) fileExistsWithMem(new File(dir, "UsesMem.UsesMem.memory.v"), Some("./mem1")) fileExistsWithMem(new File(dir, "UsesMem.UsesMemLow.memory.v"), Some("./mem2")) fileExistsWithMem(new File(dir, "firrtl_black_box_resource_files.f")) } "Calling a module that loads memories from a file more than once should work" in { val testDirName = "test_run_dir/load_three_memory_spec" val result = (new ChiselStage).execute( args = Array("-X", "verilog", "--target-dir", testDirName), annotations = Seq(ChiselGeneratorAnnotation(() => new UsesThreeMems(memoryDepth = 8, memoryType = UInt(16.W)))) ) val dir = new File(testDirName) fileExistsWithMem( new File(dir, "UsesThreeMems.UsesThreeMems.memory1.v"), Some("./mem1")) fileExistsWithMem( new File(dir, "UsesThreeMems.UsesThreeMems.memory2.v"), Some("./mem1")) fileExistsWithMem( new File(dir, "UsesThreeMems.UsesThreeMems.memory3.v"), Some("./mem1")) fileExistsWithMem( new File(dir, "firrtl_black_box_resource_files.f")) } "In this example the memory has a complex memory type containing a bundle" in { val complexTestDirName = "test_run_dir/complex_memory_load" val result = (new ChiselStage).execute( args = Array("-X", "verilog", "--target-dir", complexTestDirName), annotations = Seq(ChiselGeneratorAnnotation(() => new HasComplexMemory(memoryDepth = 8))) ) val dir = new File(complexTestDirName) val memoryElements = Seq("a", "b", "c") memoryElements.foreach { element => val file = new File(dir, s"HasComplexMemory.HasComplexMemory.memory_$element.v") file.exists() should be (true) val fileText = io.Source.fromFile(file).getLines().mkString("\n") fileText should include (s"""$$readmemh("./mem_$element", HasComplexMemory.memory_$element);""") file.delete() } } "Has binary format support" in { val testDirName = "test_run_dir/binary_memory_load" val result = (new ChiselStage).execute( args = Array("-X", "verilog", "--target-dir", testDirName), annotations = Seq(ChiselGeneratorAnnotation(() => new HasBinarySupport(memoryDepth = 8, memoryType = UInt(16.W)))) ) val dir = new File(testDirName) val file = new File(dir, s"HasBinarySupport.HasBinarySupport.memory.v") file.exists() should be (true) val fileText = io.Source.fromFile(file).getLines().mkString("\n") fileText should include (s"""$$readmemb("./mem", HasBinarySupport.memory);""") 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);""") } }