// See LICENSE for license details. package chiselTests import java.io.File import chisel3._ import chisel3.util.experimental.loadMemoryFromFile import chisel3.util.log2Ceil import firrtl.FirrtlExecutionSuccess import firrtl.annotations.MemoryLoadFileType import org.scalatest.{FreeSpec, 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 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) } /** * 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 FreeSpec 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 = Driver.execute( args = Array("-X", "verilog", "--target-dir", testDirName), dut = () => new UsesMem(memoryDepth = 8, memoryType = UInt(16.W)) ) result match { case ChiselExecutionSuccess(_, _, Some(FirrtlExecutionSuccess(_, _))) => 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")) case _=> throw new Exception("Failed compile") } } "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 = Driver.execute( args = Array("-X", "verilog", "--target-dir", testDirName), dut = () => new UsesThreeMems(memoryDepth = 8, memoryType = UInt(16.W)) ) result match { case ChiselExecutionSuccess(_, _, Some(FirrtlExecutionSuccess(_, _))) => 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")) case _=> throw new Exception("Failed compile") } } "In this example the memory has a complex memory type containing a bundle" in { val complexTestDirName = "test_run_dir/complex_memory_load" val result = Driver.execute( args = Array("-X", "verilog", "--target-dir", complexTestDirName), dut = () => new HasComplexMemory(memoryDepth = 8) ) result match { case ChiselExecutionSuccess(_, _, Some(FirrtlExecutionSuccess(emitType, firrtlEmitted))) => 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() } case _=> fail(s"Failed compile") } } }