Skip to content
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

Wrong memory initialization with ChiselSim #4340

Open
rameloni opened this issue Aug 12, 2024 · 2 comments
Open

Wrong memory initialization with ChiselSim #4340

rameloni opened this issue Aug 12, 2024 · 2 comments
Labels

Comments

@rameloni
Copy link
Contributor

I am trying to use chisel3.util.experimental.loadMemoryFromFileInline to initialize memory.
When I use it with ChiselSim, the memory seems to not be initialized properly, so I inspected the output VCD and I made some tests. Here is what I found:

  • Case 1: When I don't use any peek/poke or step function the memory is initialized properly.
  • Case 2: When I use any peek/poke or step function the memory seems to be initialized at the end. I inspected the VCD file and the initial value is available only at the end of the simulation. However, the Verilog code generated has a proper initial block with the correct path.

Respective Chisel/ChiselSim code

import chisel3._
import tywaves.simulator.TywavesSimulator._
import tywaves.simulator.simulatorSettings._

import org.scalatest.flatspec.AnyFlatSpec

import chisel3.util.experimental.loadMemoryFromFileInline   // <<-- new import here
class UsesMem(memoryDepth: Int, memoryType: Data, filename: String) 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, filename)  // <<-- Note the annotation here
}
class LoadMemory extends AnyFlatSpec {
  val bits = 64
  val words = 4
  val filename = System.getProperty("user.dir") + "/MemoryInit.hex"
  val frequency = 50000000


  scala.reflect.io.File(filename).writeAll("0001020304050607\r\n08090A0B0C0D0E0F\r\n0F0E0D0C0B0A0908\r\n8080808080808080\r\n")

  behavior of "LoadMemory"
  it should "pass a unit test" in {
    simulate(new UsesMem(words, UInt(bits.W), filename), Seq(VcdTrace, WithTywavesWaveforms(true), WithFirtoolArgs(Seq("--disable-all-randomization")), SaveWorkdirFile("workDir")))
      { m =>
        m.clock.step()
        m.reset.poke(true.B)
        m.clock.step()
        m.reset.poke(false.B)
        m.clock.step()
        m.clock.step()
        m.clock.step()
        m.io.address.poke(0.U)
        m.clock.step()
        m.io.address.poke(2.U)
    }
  }
}

VCD Case 1: No peek/poke or step

// ...
    $scope module memory_ext $end
     $var wire 64 # Memory[0] [63:0] $end
     $var wire 64 % Memory[1] [63:0] $end
     $var wire 64 ' Memory[2] [63:0] $end
     $var wire 64 ) Memory[3] [63:0] $end
     $var wire  2 / R0_addr [1:0] $end
     $var wire  1 0 R0_clk $end
     $var wire 64 + R0_data [63:0] $end
     $var wire  1 2 R0_en $end
    $upscope $end
// ...
#0
b0000000000000001000000100000001100000100000001010000011000000111 #
b0000100000001001000010100000101100001100000011010000111000001111 %
b0000111100001110000011010000110000001011000010100000100100001000 '
b1000000010000000100000001000000010000000100000001000000010000000 )
b0000000000000001000000100000001100000100000001010000011000000111 +
b0000000000000000000000000000000000000000000000000000000000000000 -
b00 /
00
01
12

VCD Case 2: using any peek/poke or step causes initialization in the end

// ...
    $scope module memory_ext $end
     $var wire 64 # Memory[0] [63:0] $end
     $var wire 64 % Memory[1] [63:0] $end
     $var wire 64 ' Memory[2] [63:0] $end
     $var wire 64 ) Memory[3] [63:0] $end
     $var wire  2 / R0_addr [1:0] $end
     $var wire  1 0 R0_clk $end
     $var wire 64 + R0_data [63:0] $end
     $var wire  1 2 R0_en $end
    $upscope $end
// ...

#0
b0000000000000000000000000000000000000000000000000000000000000000 #
b0000000000000000000000000000000000000000000000000000000000000000 %
b0000000000000000000000000000000000000000000000000000000000000000 '
b0000000000000000000000000000000000000000000000000000000000000000 )
b0000000000000000000000000000000000000000000000000000000000000000 +
b0000000000000000000000000000000000000000000000000000000000000000 -
b00 /
00
01
12
#1
10
#2
b0000000000000001000000100000001100000100000001010000011000000111 #
b0000100000001001000010100000101100001100000011010000111000001111 %
b0000111100001110000011010000110000001011000010100000100100001000 '
b1000000010000000100000001000000010000000100000001000000010000000 )
b0000000000000001000000100000001100000100000001010000011000000111 +
@uenoku
Copy link
Contributor

uenoku commented Aug 13, 2024

I think this bug has same root cause as #3962. Currently ChiselSim runs simulation main body from an initial block in testharness so initial blocks in other modules may or may not be executed.

@rameloni
Copy link
Contributor Author

rameloni commented Aug 13, 2024

I think this bug has same root cause as #3962. Currently ChiselSim runs simulation main body from an initial block in testharness so initial blocks in other modules may or may not be executed.

Yes, I think it's the same problem. I haven't seen it before.
Is there any way to make it working atm? I found a workaround which consists of reading the file from Scala and initializing the memory during the reset signal. But it's not the best way I guess.

class UsesMem(memoryDepth: Int, memoryType: Data, filename: String) 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)


  

  
  def readFileToSequence(filename: String): Seq[BigInt] = {
    val bufferedSource = scala.io.Source.fromFile(filename)
    try {
      bufferedSource.getLines().map(line => BigInt(line, 16)).toSeq
    } finally {
      bufferedSource.close()
    }
  }
  
  // Set the memory
  val memContent = readFileToSequence(filename)
  when(reset.asBool) {
    if(memContent.nonEmpty) {
      // Read the file and initialize it
      val s = if (memContent.length < memoryDepth) memContent.length else words
      for (i <- 0 until s) {
        memory(i) := memContent(i).asUInt
      }
    }
  }
} 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants