You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chisel 6.2.0中Hex文件初始化内存测试失败求助

Chisel 6.2.0中loadMemoryFromFileInline加载Hex文件初始化内存测试失败问题

在Chisel 6.2.0中使用loadMemoryFromFileInline加载Hex文件初始化内存时,测试代码无法读取到预期数据,已确认文件路径无误,但功能不生效。相同代码在Chisel 5.1.0+chiseltest环境下可正常运行。

项目结构

example
└── src
│   └── hex
│   │   └── example.hex.txt
│   └── main/scala/circuit
│   │             └── IMemory.scala
│   └── test/scala/circuit
│                 └── IMemorySpec.scala
└── build.sbt

相关代码

build.sbt(Chisel 6.2.0环境)

ThisBuild / scalaVersion := "2.13.12"
ThisBuild / version := "0.1.0"
ThisBuild / organization := "%ORGANIZATION%"

val chiselVersion = "6.2.0"

lazy val root = (project in file("."))
  .settings(
    name := "%NAME%",
    libraryDependencies ++= Seq(
      "org.chipsalliance" %% "chisel" % chiselVersion,
      "org.scalatest" %% "scalatest" % "3.2.16" % "test"
    ),
    scalacOptions ++= Seq(
      "-language:reflectiveCalls",
      "-deprecation",
      "-feature",
      "-Xcheckinit",
      "-Ymacro-annotations"
    ),
    addCompilerPlugin(
      "org.chipsalliance" % "chisel-plugin" % chiselVersion cross CrossVersion.full
    )
  )

example.hex.txt

003100b3
40628033
06432283
06512423
064000ef

IMemory.scala

package circuit

import chisel3._
import chisel3.util.experimental.loadMemoryFromFileInline
import circt.stage.ChiselStage

class IMemoryIO extends Bundle {
  val rdAddr = Input(UInt(5.W))
  val rdData = Output(UInt(32.W))
  val wrAddr = Input(UInt(5.W))
  val wrData = Input(UInt(32.W))
  val wrEna = Input(UInt(1.W))
}

class IMemory(memoryFile: String = "") extends Module {
  val io = IO(new IMemoryIO)
  val mem = SyncReadMem(1024, UInt(32.W))
  io.rdData := mem.read(io.rdAddr)
  io.rdData := mem(io.rdAddr)
  when(io.wrEna === 1.U) {
    mem.write(io.wrAddr, io.wrData)
  }
  if (memoryFile.trim().nonEmpty) {
    loadMemoryFromFileInline(mem, memoryFile)
  }
}

IMemorySpec.scala(Chisel 6.2.0环境)

package circuit
import chisel3._
import chisel3.experimental.BundleLiterals._
import chisel3.simulator.EphemeralSimulator._
import org.scalatest.freespec.AnyFreeSpec
import org.scalatest.matchers.must.Matchers
import java.io.File
class IMemorySpec extends AnyFreeSpec with Matchers {
  "write memory" in {
    simulate(new IMemory("src/hex/example.hex.txt")) { dut =>
      dut.io.wrAddr.poke(4.U)
      dut.io.wrEna.poke(1.U)
      dut.io.wrData.poke(0x003100b3.U)
      dut.clock.step()
      dut.io.rdAddr.poke(4.U)
      dut.clock.step()
      dut.io.rdData.expect(0x003100b3.U)
    }
  }
  "loadMemory from file" in {
    simulate(new IMemory("src/hex/example.hex.txt")) { dut =>
      dut.io.rdAddr.poke(0.U)
      dut.clock.step()
      dut.io.rdData.expect(0x003100b3.U)
    }
  }
}

错误输出

运行sbt "testOnly circuit.IMemorySpec"后,测试失败:

[info] - file should exist
[info] - write memory
[info] - loadMemory from file *** FAILED ***
[info]   chisel3.simulator.PeekPokeAPI$FailedExpectationException: Failed Expectation: Observed value '0' != 3211443. Expectation failed: observed value 0 != 3211443

Chisel 5.1.0环境下可行方案

build.sbt(Chisel 5.1.0环境)

ThisBuild / scalaVersion := "2.13.12"
ThisBuild / version := "0.1.0"
ThisBuild / organization := "%ORGANIZATION%"

val chiselVersion = "5.1.0"

lazy val root = (project in file("."))
  .settings(
    name := "%NAME%",
    libraryDependencies ++= Seq(
      "org.chipsalliance" %% "chisel" % chiselVersion,
      "edu.berkeley.cs" %% "chiseltest" % "5.0.2" % "test"
    ),
    scalacOptions ++= Seq(
      "-language:reflectiveCalls",
      "-deprecation",
      "-feature",
      "-Xcheckinit",
      "-Ymacro-annotations"
    ),
    addCompilerPlugin(
      "org.chipsalliance" % "chisel-plugin" % chiselVersion cross CrossVersion.full
    )
  )

测试代码(Chisel 5.1.0环境)

package circuit

import chisel3._
import chiseltest._

import org.scalatest.freespec.AnyFreeSpec
import chisel3.experimental.BundleLiterals._

class IMemorySpec extends AnyFreeSpec with ChiselScalatestTester {
  "memory initial should work by example.hex.txt" in {
    test(new IMemory("src/hex/example.hex.txt")) { dut =>
      dut.io.rdAddr.poke(0.U)
      dut.clock.step()
      dut.io.rdData.expect(0x003100b3.U)
      dut.io.rdAddr.poke(1.U)
      dut.clock.step()
      dut.io.rdData.expect(0x40628033.U)
      dut.io.rdAddr.poke(2.U)
      dut.clock.step()
      dut.io.rdData.expect(0x06432283.U)
      dut.io.rdAddr.poke(3.U)
      dut.clock.step()
      dut.io.rdData.expect(0x06512423.U)
    }
  }
}

解决方案

Chisel 6.x版本中,默认的EphemeralSimulator对loadMemoryFromFileInline的内存初始化支持有限,切换回chiseltest测试框架即可解决问题,步骤如下:

1. 更新build.sbt

添加与Chisel版本匹配的chiseltest依赖:

ThisBuild / scalaVersion := "2.13.12"
ThisBuild / version := "0.1.0"
ThisBuild / organization := "%ORGANIZATION%"

val chiselVersion = "6.2.0"

lazy val root = (project in file("."))
  .settings(
    name := "%NAME%",
    libraryDependencies ++= Seq(
      "org.chipsalliance" %% "chisel" % chiselVersion,
      "org.scalatest" %% "scalatest" % "3.2.16" % "test",
      "edu.berkeley.cs" %% "chiseltest" % chiselVersion % "test"
    ),
    scalacOptions ++= Seq(
      "-language:reflectiveCalls",
      "-deprecation",
      "-feature",
      "-Xcheckinit",
      "-Ymacro-annotations"
    ),
    addCompilerPlugin(
      "org.chipsalliance" % "chisel-plugin" % chiselVersion cross CrossVersion.full
    )
  )

2. 修改测试代码

改用chiseltest的API编写测试:

package circuit

import chisel3._
import chiseltest._
import org.scalatest.freespec.AnyFreeSpec
import chisel3.experimental.BundleLiterals._

class IMemorySpec extends AnyFreeSpec with ChiselScalatestTester {
  "write memory" in {
    test(new IMemory("src/hex/example.hex.txt")) { dut =>
      dut.io.wrAddr.poke(4.U)
      dut.io.wrEna.poke(1.U)
      dut.io.wrData.poke(0x003100b3.U)
      dut.clock.step()
      dut.io.rdAddr.poke(4.U)
      dut.clock.step()
      dut.io.rdData.expect(0x003100b3.U)
    }
  }

  "loadMemory from file" in {
    test(new IMemory("src/hex/example.hex.txt")) { dut =>
      dut.io.rdAddr.poke(0.U)
      dut.clock.step()
      dut.io.rdData.expect(0x003100b3.U)
    }
  }
}

3. 额外优化

IMemory.scala中存在冗余代码:io.rdData := mem.read(io.rdAddr)和io.rdData := mem(io.rdAddr)重复,mem(io.rdAddr)等价于调用read方法,保留其中一行即可:

// 保留其中一行,比如:
io.rdData := mem(io.rdAddr)

4. 验证测试

重新运行测试命令:

sbt "testOnly circuit.IMemorySpec"

内容的提问来源于stack exchange,提问作者Fang Yongrui

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 12:35:54