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

【分享】接入difftest的几个主要步骤 #8

Open
Undefined01 opened this issue Jul 21, 2021 · 23 comments
Open

【分享】接入difftest的几个主要步骤 #8

Undefined01 opened this issue Jul 21, 2021 · 23 comments
Labels
#Difftest Difftest 相关问题 知识分享 该issue的主题是信息、知识分享

Comments

@Undefined01
Copy link

Undefined01 commented Jul 21, 2021

写在前面

我个人使用 chisel 编写项目,并且使用 mill 作为构建工具。

使用 verilog 编写或者使用 sbt 构建工具大同小异。

如果在操作过程有出现本文章未提及的问题,欢迎在讨论区提出。你提出的问题很可能能帮助到别人。

下载 difftest 项目和所需依赖

主要为 difftest 项目NEMU 模拟器

其中,difftest 项目必须在 chisel 项目根目录下,NEMU 项目不限制位置,但是必须设置 NEMU_HOME 环境变量为 NEMU 所在目录。

此外,还需要设置 NOOP_HOME 为项目根目录(参考 verilog.mk,虽然不会使用这个功能,但是这个变量必须要有)。

我个人的项目树大致如下,不要求完全一致,符合上述要求即可。

/home/xxx/
  chisel-template/
    difftest/
    src/
    build.sc
  NEMU/

因此 NEMU_HOME 环境变量应该设置为 /home/xxx/NEMUNOOP_HOME 环境变量应该设置为 /home/xxx/chisel-template

(正式开始前建议执行 git commit -am --allow-empty "before difftest" 保证接入失败后可以随时回退回原来的状态)

构建 SimTop.v

由于 difftest 框架本身由 verilog 写成,仅使用 chisel 编写的项目需要此步骤。使用 verilog 编写的项目可以直接跳过此步骤,但是必须保证 build/SimTop.v 是 CPU 的顶级文件且具有 规定的端口

我的 SimTop.v 实现如下:

package sim

import chisel3._
import rvcore._
import difftest._

class SimTop extends Module {
  // 规定的端口格式,不多不少,但可以暂时不使用。
  val io = IO(new Bundle {
    val logCtrl = new LogCtrlIO
    val perfInfo = new PerfInfoIO
    val uart = new UARTIO
  })

  val rvcore = Module(new RvCore)

  // rvcore 访问内存的端口,下个步骤会说明,此处暂不连接
  rvcore.io.ram.rdata := 0.U

  // 暂不使用 difftest 给出的端口,但是还是需要初始化合法值
  io.uart.in.valid := false.B
  io.uart.out.valid := false.B
  io.uart.out.ch := 0.U
}

由于需要使用 difftest 模块下的文件,需要指示 mill difftest 模块的位置。修改 build.sc 如下(sbt 需修改 build.sbt,两个文件不通用)

// import Mill dependency
import mill._
import mill.scalalib._
import mill.scalalib.scalafmt.ScalafmtModule
import mill.scalalib.TestModule.ScalaTest
// support BSP
import mill.bsp._

object ysyx extends SbtModule { m =>
  override def millSourcePath = os.pwd
  override def scalaVersion = "2.12.13"
  override def scalacOptions = Seq(
    "-Xsource:2.11",
    "-language:reflectiveCalls",
    "-deprecation",
    "-feature",
    "-Xcheckinit",
    // Enables autoclonetype2 in 3.4.x (on by default in 3.5)
    "-P:chiselplugin:useBundlePlugin"
  )
  override def ivyDeps = Agg(
    ivy"edu.berkeley.cs::chisel3:3.4.3"
  )
  override def scalacPluginIvyDeps = Agg(
    ivy"edu.berkeley.cs:::chisel3-plugin:3.4.3",
    ivy"org.scalamacros:::paradise:2.1.1"
  )
  object test extends Tests with ScalaTest {
    override def ivyDeps = m.ivyDeps() ++ Agg(
      ivy"edu.berkeley.cs::chiseltest:0.3.3"
    )
  }
  override def moduleDeps = super.moduleDeps ++ Seq(difftest)
}

object difftest extends ScalaModule {
  override def scalaVersion = "2.12.13"
  override def millSourcePath = os.pwd / "difftest"
  override def ivyDeps = Agg(
    ivy"edu.berkeley.cs::chisel3:3.4.3"
  )
}

此时,mill 可以执行以下命令生成 build/SimTop.v

mill -i __.test.runMain top.TopMain -td ./build

其中,top.TopMain 是本 chisel 项目的顶层文件,形如

package top

import chisel3.stage._
import sim._

object TopMain extends App {
  (new ChiselStage).execute(
    args,
    Seq(
      ChiselGeneratorAnnotation(() => new SimTop())
    )
  )
}

SimTop 是编写的 CPU 模块。

运行上述命令后,期望能找到 build/SimTop.v

构建 difftest 程序 build/emu

在项目根目录中执行以下命令即可构建 build/emu

make -C difftest emu

在准备好上述文件后,理论上就算不做任何修改,在没有接入 difftest 的情况下期望能生成 build/emu 文件(可能会有大量输出,成功与否以 build/emu 文件是否存在为准)。如果无法生成,请检查是否做好上述准备,如是否已配置正确的 NEMU_HOME 环境变量、build/SimTop.v 是否是 CPU 模块的顶层文件等。

此时,运行 build/emu --help 应该能够看到帮助文档。

$ build/emu --help
Emu compiled at Jul 21 2021, 22:18:14
Usage: build/emu [OPTION...]

  -s, --seed=NUM             use this seed
  -C, --max-cycles=NUM       execute at most NUM cycles
  -I, --max-instr=NUM        execute at most NUM instructions
  -W, --warmup-instr=NUM     the number of warmup instructions
  -D, --stat-cycles=NUM      the interval cycles of dumping statistics
  -i, --image=FILE           run with this image file
  -b, --log-begin=NUM        display log from NUM th cycle
  -e, --log-end=NUM          stop display log at NUM th cycle
      --force-dump-result    force dump performance counter result in the end
      --load-snapshot=PATH   load snapshot from PATH
      --no-snapshot          disable saving snapshots
      --dump-wave            dump waveform when log is enabled
      --no-diff              disable differential testing
      --diff=PATH            set the path of REF for differential testing
  -h, --help                 print program help info

准备镜像文件

任意镜像文件均可,可以使用 AbstractMachine 编译 cpu-test 作为镜像(需要实现几乎全部 rv64i 指令才能正常运行),也可以自己编写并生成 stripped 的纯二进制文件。该文件会作为 difftest 过程中执行的程序。

也可以使用框架里的 inst.bin 替代。

以下使用 $IMG 代指该镜像文件。

此时执行 build/emu -i $IMG,期望看到以下输出。

$ build/emu -i ../oscpu-framework/cpu/inst.bin 
Emu compiled at Jul 21 2021, 22:18:14
The image is ../oscpu-framework/cpu/inst.bin
Using simulated 8192MB RAM
[warning] sdcard img not found
--diff is not given, try to use $(NEMU_HOME)/build/riscv64-nemu-interpreter-so by default
Using /home/lh/NEMU/build/riscv64-nemu-interpreter-so for difftest
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'clint' at [0xa2000000, 0xa200ffff]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'sdhci' at [0xa3000000, 0xa300007f]
[src/device/sdcard.c,118,init_sdcard] Can not find sdcard image: /home/yzh/projectn/debian.img
No instruction commits for 5000 cycles of core 0. Please check the first instruction.
Note: The first instruction may lie in 0x10000000 which may executes and commits after 500 cycles.
   Or the first instruction may lie in 0x80000000 which may executes and commits after 2000 cycles.

============== Commit Group Trace (Core 0) ==============
commit group [0]: pc 0000000000 cmtcnt 0 
commit group [1]: pc 0000000000 cmtcnt 0 
commit group [2]: pc 0000000000 cmtcnt 0 
commit group [3]: pc 0000000000 cmtcnt 0 
commit group [4]: pc 0000000000 cmtcnt 0 
commit group [5]: pc 0000000000 cmtcnt 0 
commit group [6]: pc 0000000000 cmtcnt 0 
commit group [7]: pc 0000000000 cmtcnt 0 
commit group [8]: pc 0000000000 cmtcnt 0 
commit group [9]: pc 0000000000 cmtcnt 0 
commit group [a]: pc 0000000000 cmtcnt 0 
commit group [b]: pc 0000000000 cmtcnt 0 
commit group [c]: pc 0000000000 cmtcnt 0 
commit group [d]: pc 0000000000 cmtcnt 0 
commit group [e]: pc 0000000000 cmtcnt 0 
commit group [f]: pc 0000000000 cmtcnt 0 <--

============== Commit Instr Trace ==============
commit inst [0]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [1]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [2]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [3]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [4]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [5]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [6]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [7]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [8]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [9]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [a]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [b]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [c]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [d]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [e]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [f]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 <--

==============  REF Regs  ==============
  $0: 0x0000000000000000   ra: 0x0000000000000000   sp: 0x0000000000000000   gp: 0x0000000000000000 
  tp: 0x0000000000000000   t0: 0x0000000000000000   t1: 0x0000000000000000   t2: 0x0000000000000000 
  s0: 0x0000000000000000   s1: 0x0000000000000000   a0: 0x0000000000000000   a1: 0x0000000000000000 
  a2: 0x0000000000000000   a3: 0x0000000000000000   a4: 0x0000000000000000   a5: 0x0000000000000000 
  a6: 0x0000000000000000   a7: 0x0000000000000000   s2: 0x0000000000000000   s3: 0x0000000000000000 
  s4: 0x0000000000000000   s5: 0x0000000000000000   s6: 0x0000000000000000   s7: 0x0000000000000000 
  s8: 0x0000000000000000   s9: 0x0000000000000000  s10: 0x0000000000000000  s11: 0x0000000000000000 
  t3: 0x0000000000000000   t4: 0x0000000000000000   t5: 0x0000000000000000   t6: 0x0000000000000000 
 ft0: 0x0000000000000000  ft1: 0x0000000000000000  ft2: 0x0000000000000000  ft3: 0x0000000000000000 
 ft4: 0x0000000000000000  ft5: 0x0000000000000000  ft6: 0x0000000000000000  ft7: 0x0000000000000000 
 fs0: 0x0000000000000000  fs1: 0x0000000000000000  fa0: 0x0000000000000000  fa1: 0x0000000000000000 
 fa2: 0x0000000000000000  fa3: 0x0000000000000000  fa4: 0x0000000000000000  fa5: 0x0000000000000000 
 fa6: 0x0000000000000000  fa7: 0x0000000000000000  fs2: 0x0000000000000000  fs3: 0x0000000000000000 
 fs4: 0x0000000000000000  fs5: 0x0000000000000000  fs6: 0x0000000000000000  fs7: 0x0000000000000000 
 fs8: 0x0000000000000000  fs9: 0x0000000000000000 fs10: 0x0000000000000000 fs11: 0x0000000000000000 
 ft8: 0x0000000000000000  ft9: 0x0000000000000000 ft10: 0x0000000000000000 ft11: 0x0000000000000000 
pc: 0x0000000080000000 mstatus: 0x0000000000000000 mcause: 0x0000000000000000 mepc: 0x0000000000000000
                       sstatus: 0x0000000000000000 scause: 0x0000000000000000 sepc: 0x0000000000000000
satp: 0x0000000000000000
mip: 0x0000000000000000 mie: 0x0000000000000000 mscratch: 0x0000000000000000 sscratch: 0x0000000000000000
mideleg: 0x0000000000000000 medeleg: 0x0000000000000000
mtval: 0x0000000000000000 stval: 0x0000000000000000mtvec: 0x0000000000000000 stvec: 0x0000000000000000
priviledgeMode: 0
Core 0: ABORT at pc = 0x0
total guest instructions = 0
instrCnt = 0, cycleCnt = 0, IPC = -nan
Seed=0 Guest cycle spent: 5001 (this will be different from cycleCnt if emu loads a snapshot)
Host time spent: 6ms

在前几行输出可以看出,由于我们还没有接入 difftest,框架无法获取我们 CPU 的执行状态。因此接下来需要修改 CPU 模块来接入 difftest 框架。

接入RAM

我们的 CPU 需要获取执行的指令以及运行 load/store 指令。原先这些数据verilator 中的测试框架给出,现在需要将其转接到 difftest 框架中。

difftest 框架通过 RAMHelper 向 CPU 提供读写信息。其定义在 ram.v 中。我们需要做的就是在 CPU 中实例化这个 RAMHelper 模块并且按照其定义连接对应信号。

需要注意的是,RAMHelper 读写都仅以 8字节 为一个单位,也即,当rIdx1 时,实际读取了 0x80000008 开始的八个字节。写入同理。

正确接入后,期望在 CPU 运行过程中可以正确读取对应地址的信息。正确性可以通过在代码中打印日志人工判断。

我个人在 SimTop.v 中的实现如下:

  class RAMHelper extends BlackBox {
    val io = IO(new Bundle {
      val clk = Input(Clock())
      val en = Input(Bool())
      val rIdx = Input(UInt(64.W))
      val rdata = Output(UInt(64.W))
      val wIdx = Input(UInt(64.W))
      val wdata = Input(UInt(64.W))
      val wmask = Input(UInt(64.W))
      val wen = Input(Bool())
    })
  }
  val ram = Module(new RAMHelper)
  val rvcore = Module(new RvCore)

  ram.io.clk := clock
  ram.io.en := rvcore.io.ram.en
  ram.io.rIdx := rvcore.io.ram.raddr - (BigInt("80000000", 16) >> 3).U
  ram.io.wIdx := rvcore.io.ram.waddr - (BigInt("80000000", 16) >> 3).U
  ram.io.wdata := rvcore.io.ram.wdata
  ram.io.wmask := rvcore.io.ram.wmask
  ram.io.wen := rvcore.io.ram.wen
  rvcore.io.ram.rdata := ram.io.rdata

接入“指令提交”

我们需要通过一个方式告诉 difftest 框架已经完成了某条指令的执行。该功能通过 DifftestInstrCommit 实现。verilog中对应的模块是 difftest.v

具体而言,需要在写回阶段,实例化 DifftestInstrCommit 并且连接对应的信号。

我个人的实现大概如下:

val commit = Module(new difftest.DifftestInstrCommit)
commit.io.clock := clock
commit.io.coreid := 0.U
commit.io.index := 0.U

commit.io.valid := RegNext(io.in_valid && !io.stall)
commit.io.pc := RegNext(io.in.pc)
commit.io.instr := DontCare
commit.io.skip := false.B
commit.io.isRVC := false.B
commit.io.scFailed := false.B
commit.io.wen :=
  RegNext(io.in_valid && io.in.wb.rd =/= 0.U)
commit.io.wdata := RegNext(io.in.wb.data)
commit.io.wdest := RegNext(io.in.wb.rd)

其中,我没有使用 RegNext 的信号是暂时不需要管的,和我一致即可。其余使用了 RegNext 的信号需要替换成你们对应的实现。(端口含义通过命名可以猜出来很多了)

此时,执行 mill -i __.test.runMain top.TopMain -td ./buildmake -C difftest emubuild/emu -i $IMG,期望 difftest 能够正确识别出第一条指令的执行。但是由于其余数据我们还未提供给 difftest 框架,所以极可能第一条指令就会被判错,这是正常现象。

该阶段正确性的判定可以通过是否输出了 The first instruction of core 0 has commited. Difftest enabled. 判断。

我个人在该阶段的输出如下:

$ build/emu -b 0 -i ../oscpu-framework/cpu/inst.bin
Emu compiled at Jul 21 2021, 23:00:47
The image is ../oscpu-framework/cpu/inst.bin
Using simulated 8192MB RAM
[warning] sdcard img not found
--diff is not given, try to use $(NEMU_HOME)/build/riscv64-nemu-interpreter-so by default
Using /home/lh/NEMU/build/riscv64-nemu-interpreter-so for difftest
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'clint' at [0xa2000000, 0xa200ffff]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'sdhci' at [0xa3000000, 0xa300007f]
[src/device/sdcard.c,118,init_sdcard] Can not find sdcard image: /home/yzh/projectn/debian.img
Received commit pc=80000000
The first instruction of core 0 has commited. Difftest enabled. 

============== Commit Group Trace (Core 0) ==============
commit group [0]: pc 0080000000 cmtcnt 1 <--
commit group [1]: pc 0000000000 cmtcnt 0 
commit group [2]: pc 0000000000 cmtcnt 0 
commit group [3]: pc 0000000000 cmtcnt 0 
commit group [4]: pc 0000000000 cmtcnt 0 
commit group [5]: pc 0000000000 cmtcnt 0 
commit group [6]: pc 0000000000 cmtcnt 0 
commit group [7]: pc 0000000000 cmtcnt 0 
commit group [8]: pc 0000000000 cmtcnt 0 
commit group [9]: pc 0000000000 cmtcnt 0 
commit group [a]: pc 0000000000 cmtcnt 0 
commit group [b]: pc 0000000000 cmtcnt 0 
commit group [c]: pc 0000000000 cmtcnt 0 
commit group [d]: pc 0000000000 cmtcnt 0 
commit group [e]: pc 0000000000 cmtcnt 0 
commit group [f]: pc 0000000000 cmtcnt 0 

============== Commit Instr Trace ==============
commit inst [0]: pc 0080000000 inst 00000000 wen 1 dst 00000001 data 0000000000000001 <--
commit inst [1]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [2]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [3]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [4]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [5]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [6]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [7]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [8]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [9]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [a]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [b]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [c]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [d]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [e]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 
commit inst [f]: pc 0000000000 inst 00000000 wen 0 dst 00000000 data 0000000000000000 

==============  REF Regs  ==============
  $0: 0x0000000000000000   ra: 0x0000000000000001   sp: 0x0000000000000000   gp: 0x0000000000000000 
  tp: 0x0000000000000000   t0: 0x0000000000000000   t1: 0x0000000000000000   t2: 0x0000000000000000 
  s0: 0x0000000000000000   s1: 0x0000000000000000   a0: 0x0000000000000000   a1: 0x0000000000000000 
  a2: 0x0000000000000000   a3: 0x0000000000000000   a4: 0x0000000000000000   a5: 0x0000000000000000 
  a6: 0x0000000000000000   a7: 0x0000000000000000   s2: 0x0000000000000000   s3: 0x0000000000000000 
  s4: 0x0000000000000000   s5: 0x0000000000000000   s6: 0x0000000000000000   s7: 0x0000000000000000 
  s8: 0x0000000000000000   s9: 0x0000000000000000  s10: 0x0000000000000000  s11: 0x0000000000000000 
  t3: 0x0000000000000000   t4: 0x0000000000000000   t5: 0x0000000000000000   t6: 0x0000000000000000 
 ft0: 0x0000000000000000  ft1: 0x0000000000000000  ft2: 0x0000000000000000  ft3: 0x0000000000000000 
 ft4: 0x0000000000000000  ft5: 0x0000000000000000  ft6: 0x0000000000000000  ft7: 0x0000000000000000 
 fs0: 0x0000000000000000  fs1: 0x0000000000000000  fa0: 0x0000000000000000  fa1: 0x0000000000000000 
 fa2: 0x0000000000000000  fa3: 0x0000000000000000  fa4: 0x0000000000000000  fa5: 0x0000000000000000 
 fa6: 0x0000000000000000  fa7: 0x0000000000000000  fs2: 0x0000000000000000  fs3: 0x0000000000000000 
 fs4: 0x0000000000000000  fs5: 0x0000000000000000  fs6: 0x0000000000000000  fs7: 0x0000000000000000 
 fs8: 0x0000000000000000  fs9: 0x0000000000000000 fs10: 0x0000000000000000 fs11: 0x0000000000000000 
 ft8: 0x0000000000000000  ft9: 0x0000000000000000 ft10: 0x0000000000000000 ft11: 0x0000000000000000 
pc: 0x0000000080000004 mstatus: 0x0000000000000000 mcause: 0x0000000000000000 mepc: 0x0000000000000000
                       sstatus: 0x0000000000000000 scause: 0x0000000000000000 sepc: 0x0000000000000000
satp: 0x0000000000000000
mip: 0x0000000000000000 mie: 0x0000000000000000 mscratch: 0x0000000000000000 sscratch: 0x0000000000000000
mideleg: 0x0000000000000000 medeleg: 0x0000000000000000
mtval: 0x0000000000000000 stval: 0x0000000000000000mtvec: 0x0000000000000000 stvec: 0x0000000000000000
priviledgeMode: 0
     ra different at pc = 0x0080000000, right= 0x0000000000000001, wrong = 0x0000000000000000
Core 0: ABORT at pc = 0x0
total guest instructions = 0
instrCnt = 0, cycleCnt = 0, IPC = -nan
Seed=0 Guest cycle spent: 5 (this will be different from cycleCnt if emu loads a snapshot)
Host time spent: 4ms

从错误信息中,可以看到期望的 Difftest enabled,说明 InstrCommit 正确接入了。并且可以看到在 ==Commit Instr Trace== 中我的CPU正确提交了第一条指令,但是由于 ra 寄存器的错误终止了 difftest。这个问题在下个阶段会修复。

接入寄存器组

difftest 框架主要比对我们 CPU 的寄存器值和 NEMU 模拟器的寄存器值来判定实现的正确性。该功能通过 DifftestArchIntRegState 实现。verilog 也有对应的模块

具体而言,需要在 寄存器组 模块中,实例化 DifftestArchIntRegState 并连接对应的信号。

我个人的实现大概如下:

// 这是我原先的寄存器实现,不需要和我一致
val reg = RegInit(VecInit(Seq.fill(32)(0.U(c.XLEN.W))))

// 以下为接入 difftest 新增内容
val mod = Module(new difftest.DifftestArchIntRegState)
mod.io.clock := clock
mod.io.coreid := 0.U
mod.io.gpr := reg

此外,difftest 在运行时还会对比 CSR 寄存器的值。由于我们此时并未实现,直接硬编码即可。(但是不能留空,否则会出现非合法值导致 NEMU 异常)

    val csr = Module(new difftest.DifftestCSRState)
    csr.io.clock := clock
    csr.io.coreid := 0.U
    csr.io.mstatus := 0.U
    csr.io.mcause := 0.U
    csr.io.mepc := 0.U
    csr.io.sstatus := 0.U
    csr.io.scause := 0.U
    csr.io.sepc := 0.U
    csr.io.satp := 0.U
    csr.io.mip := 0.U
    csr.io.mie := 0.U
    csr.io.mscratch := 0.U
    csr.io.sscratch := 0.U
    csr.io.mideleg := 0.U
    csr.io.medeleg := 0.U
    csr.io.mtval:= 0.U
    csr.io.stval:= 0.U
    csr.io.mtvec := 0.U
    csr.io.stvec := 0.U
    csr.io.priviledgeMode := 0.U

此时,再次执行 mill -i __.test.runMain top.TopMain -td ./buildmake -C difftest emubuild/emu -i $IMG,此时期望能够正确执行 difftest 了。(此时的表现为 difftest 没有任何输出)
但是由于我们还未实现 Trap 通知 difftest 框架我们的程序已经终止,difftest将会一直执行下去。(因为在 AMhalt 的实现暂时是一个死循环)

关于 Trap

NJU-ProjectN 中告知框架 “程序已运行结束” 的方式是发送一个 Trap 事件。在 difftest 框架中也是如此。在没有修改的情况下,如果使用 NEMU 运行 AM 编译的程序,会出现程序运行结束之后仍然无法退出的情况。该行为在 trap.c 中定义。

$NEMU_HOME/build/riscv64-nemu-interpreter -b am-kernels/tests/cpu-tests/build/add-riscv64-mycpu.bin

可以看到,无论等待多久,NEMU 的运行都不会停止。

要修改这个行为,让程序能够自动退出,只需要在 trap.chalt 前加入一条特殊的指令即可。

void halt(int code) {
  asm volatile("mv a0, %0; .word 0x0000006b" : :"r"(code));
  while (1);
}

asm 语句在 halt 函数中添加了一条特殊指令 0x0000006b。该指令在 NEMU 中的行为是终止执行,并将 a0 寄存器的值作为程序是否正常退出的标志。 a0 为 0 时表示时正常终止,否则代表异常终止。该行为定义在 exec/special.c 中。

此时重新编译 cpu-test (可以使用 make cleanmake ARCH=riscv64-mycpu),并且再次使用 NEMU 运行该测试,可以发现 NEMU 可以正常终止,并输出 HIT GOOD TRAP

$ $NEMU_HOME/build/riscv64-nemu-interpreter -b ../am-kernels/tests/cpu-tests/build/recursion-riscv64-mycpu.bin
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'clint' at [0xa2000000, 0xa200ffff]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'sdhci' at [0xa3000000, 0xa300007f]
[src/device/sdcard.c,118,init_sdcard] Can not find sdcard image: /home/yzh/projectn/debian.img
[src/monitor/monitor.c,46,load_img] The image is ../am-kernels/tests/cpu-tests/build/recursion-riscv64-mycpu.bin
[src/monitor/monitor.c,29,welcome] Debug: OFF
[src/monitor/monitor.c,32,welcome] Build time: 15:45:21, Jul 23 2021
Welcome to riscv64-NEMU!
For help, type "help"
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'screen' at [0xa1000100, 0xa1000107]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'vmem' at [0xa0000000, 0xa00752ff]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'serial' at [0xa10003f8, 0xa10003ff]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'rtc' at [0xa1000048, 0xa1000057]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'keyboard' at [0xa1000060, 0xa1000063]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'audio' at [0xa1000200, 0xa1000217]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'audio-sbuf' at [0xa0800000, 0xa080ffff]
[src/monitor/cpu-exec.c,97,cpu_exec] nemu: HIT GOOD TRAP at pc = 0x0000000080000290


[src/monitor/cpu-exec.c,35,monitor_statistic] total guest instructions = 6552

接入 Trap

接入 difftestTrap 的流程与之前类似,只需要实例化对应的接口并连接信号即可。对应本次是 DifftestTrapEvent 模块。

暂时不需要区分 GoodTrapBadTrap,因此大部分内容都可以硬编码完成。此外,只需要保证你的 CPU 在识别到 0x0000006b 这个非标准的指令的时候不会出现异常行为即可。

我的实现如下:

val trap = Module(new DifftestTrapEvent)
trap.io.clock    := clock
trap.io.coreid   := c.CoreId.U
trap.io.valid    := io.in.commit.instr === BigInt("0000006b", 16).U
trap.io.code     := 0.U // GoodTrap
trap.io.pc       := io.in.commit.pc
trap.io.cycleCnt := 0.U
trap.io.instrCnt := 0.U

此时再次执行 difftest 相关流程,可以看到 difftest 也最终输出了 Hit Good Trap 指示程序正确结束了。

Emu compiled at Jul 23 2021, 16:49:20
The image is ../am-kernels/tests/cpu-tests/build/fact-riscv64-mycpu.bin
Using simulated 8192MB RAM
[warning] sdcard img not found
--diff is not given, try to use $(NEMU_HOME)/build/riscv64-nemu-interpreter-so by default
Using /home/lh/NEMU/build/riscv64-nemu-interpreter-so for difftest
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'clint' at [0xa2000000, 0xa200ffff]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'sdhci' at [0xa3000000, 0xa300007f]
[src/device/sdcard.c,118,init_sdcard] Can not find sdcard image: /home/yzh/projectn/debian.img
The first instruction of core 0 has commited. Difftest enabled. 
Core 0: HIT GOOD TRAP at pc = 0x8000011c
total guest instructions = 0
instrCnt = 0, cycleCnt = 0, IPC = -nan
Seed=0 Guest cycle spent: 5529 (this will be different from cycleCnt if emu loads a snapshot)
Host time spent: 9ms

最后,附带一个能够一件测试 cpu-test 中所有指令的命令:

find ../am-kernels/tests/cpu-tests/build/ -name *.bin | xargs -i build/emu -i {}

如果一次性输出太多,可以考虑重定向到某个文件中然后查找 ABORT 关键词查看错误的样例。

@lsyic
Copy link

lsyic commented Jul 22, 2021

👍👍👍期待继续更新!

@SingularityKChen
Copy link

SingularityKChen commented Jul 22, 2021

补充一个编译 build/emu 时候遇到的错误和解决:

fatal error: SDL2/SDL.h: No such file or directory
   17 | #include <SDL2/SDL.h>
      |          ^~~~~~~~~~~~
compilation terminated.

解决:

sudo apt-get install libsdl2-dev

建议此时安装以下包:

sudo apt-get install build-essential libreadline-dev libsdl2-dev libc6-dev-i386 qemu-system

如果运行 ./build/emu -i xxx 报错:

NemuProxy::NemuProxy(int): Assertion `handle' failed.

那么多半是没有编译好 NEMU/build/riscv64-nemu-interpreter-so,这个时候需要在 NEMU 根目录下编译:

make -C $NEMU_HOME ISA=riscv64 SHARE=1 ENGINE=interpreter

另外小声 bb 一下,我的 difftest 没有放在根目录下,并且也不是用的 chisel-template ,所以需要手动修改 difftest 的 makefile 文件。所以还是放在根目录下面直接一些。

@zhangyx1998 zhangyx1998 added #Difftest Difftest 相关问题 知识分享 该issue的主题是信息、知识分享 labels Jul 23, 2021
@YouTheFreedom1999
Copy link

YouTheFreedom1999 commented Jul 23, 2021

你好我的已经生成emu文件,但是在运行时出现
image
该如何解决呢,看起来像是没法分配空间

@Undefined01
Copy link
Author

@YouTheFreedom1999 看上去像是系统无法 mmap 8GB 的地址空间。你的电脑是 64 位的吗,理论上地址空间应该足够才对。

至于解决的话,可以试试看把申请的空间改小一点。对于这次项目来讲应该 256MB 也足够了。

@congrongye
Copy link

@YouTheFreedom1999 我也遇到了这个问题,我的解决方案是给Linux的swap分配更多的空间,原先是2G,给到了10G,就可以了

@YouTheFreedom1999
Copy link

@YouTheFreedom1999 我也遇到了这个问题,我的解决方案是给Linux的swap分配更多的空间,原先是2G,给到了10G,就可以了

感谢,我根据这个链接https://www.cnblogs.com/tocy/p/linux-swap-cmd-summary.html增加了swap之后,difftest可以跑起来了

@qqwert0
Copy link

qqwert0 commented Jul 23, 2021

你好,请问你知道如果用sbt作为构建工具的话,应该怎么修改build.sbt吗

@congrongye
Copy link

@YouTheFreedom1999 我也遇到了这个问题,我的解决方案是给Linux的swap分配更多的空间,原先是2G,给到了10G,就可以了

感谢,我根据这个链接https://www.cnblogs.com/tocy/p/linux-swap-cmd-summary.html增加了swap之后,difftest可以跑起来了

我昨天改完swap大小之后,今天发现虚拟机打不开了。。。ubuntu的logo过后,就一直卡在黑屏界面,左上角一个光标在闪烁,这可怎么办。。。

@SingularityKChen
Copy link

指令提交连线正确但是无法提交

这两天有空就在弄 Difftest,跟着两位的步骤走,自认为连线正确但是就是无法出现The first instruction of core 0 has commited. Difftest enabled.。今天阅读 Difftest 的源码才发现,是因为 pc 的值应该从 0x80000000 开始,而不是 0。把 pc 的初始值改了就好了。不知道还有没有小伙伴遇到跟我一样的问题…

参考 Difftest 的源码:

https://github.com/OpenXiangShan/difftest/blob/c8711873af7ddec278013c44166a95b728aa8a48/src/test/csrc/difftest/difftest.cpp#L273-L286

@lsyic
Copy link

lsyic commented Jul 25, 2021

@YouTheFreedom1999 看上去像是系统无法 mmap 8GB 的地址空间。你的电脑是 64 位的吗,理论上地址空间应该足够才对。

至于解决的话,可以试试看把申请的空间改小一点。对于这次项目来讲应该 256MB 也足够了。

你好!我用了上面的方法,但是报了一个分配物理内存的错误,这个是改成256M出现的吗?

ERROR allocating physical memory. Segmentation fault (core dumped)
emu_error allocate physical mem

@YouTheFreedom1999
Copy link

@YouTheFreedom1999 看上去像是系统无法 mmap 8GB 的地址空间。你的电脑是 64 位的吗,理论上地址空间应该足够才对。
至于解决的话,可以试试看把申请的空间改小一点。对于这次项目来讲应该 256MB 也足够了。

你好!我用了上面的方法,但是报了一个分配物理内存的错误,这个是改成256M出现的吗?

ERROR allocating physical memory. Segmentation fault (core dumped)
emu_error allocate physical mem

NEMU的内存也要改成256MB

@lsyic
Copy link

lsyic commented Jul 25, 2021

NEMU的内存也要改成256MB

确实是这样,我把NEMU/include/memory/paddr.h中的

  #ifdef _SHARE
     #define PMEM_SIZE (8 * 1024 * 1024 * 1024UL)
  #else
      #define PMEM_SIZE (256 * 1024 * 1024UL) 
  #endif

8 * 1024 改成了256,重新编译NEMU和emu就可以运行了。
这样改会不会有什么风险😀?

@YouTheFreedom1999
Copy link

YouTheFreedom1999 commented Jul 25, 2021

NEMU的内存也要改成256MB

确实是这样,我把NEMU/include/memory/paddr.h中的

  #ifdef _SHARE
     #define PMEM_SIZE (8 * 1024 * 1024 * 1024UL)
  #else
      #define PMEM_SIZE (256 * 1024 * 1024UL) 
  #endif

8 * 1024 改成了256,重新编译NEMU和emu就可以运行了。
这样改会不会有什么风险😀?

有很多宏编译处理,依赖_SHAER这个变量,直接改数据怕是会出现问题
image
于是我查看了NEMU的Makefile猜测第25行中 -D_SHARE=1 就是定义的_SHARE,把它去掉

SO_CFLAGS = -fPIC
之后重新编译NEMU
make -C $NEMU_HOME ISA=riscv64 SHARE=1 ENGINE=interpreter
然后指定仿真内存为256MB重新生成
这样应该就没问题了

@ColsonZhang
Copy link

ColsonZhang commented Jul 26, 2021

你好,请问你知道如果用sbt作为构建工具的话,应该怎么修改build.sbt吗
@qqwert0

我来分享一下使用sbt接入difftest的方法。

  1. 在difftest目录下创建文件build.sbt,然后复制下面代码进去。
// See README.md for license details.

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

lazy val difftest = (project in file("."))
  .settings(
    name := "%NAME%",
    libraryDependencies ++= Seq(
      "edu.berkeley.cs" %% "chisel3" % "3.4.3",
      "edu.berkeley.cs" %% "chiseltest" % "0.3.3" % "test"
    ),
    scalacOptions ++= Seq(
      "-Xsource:2.11",
      "-language:reflectiveCalls",
      "-deprecation",
      "-feature",
      "-Xcheckinit",
      // Enables autoclonetype2 in 3.4.x (on by default in 3.5)
      "-P:chiselplugin:useBundlePlugin"
    ),
    addCompilerPlugin("edu.berkeley.cs" % "chisel3-plugin" % "3.4.3" cross CrossVersion.full),
    addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)
  )
  1. 修改项目根目录(chisel-template/)下的build.sbt,将其修改为:
// See README.md for license details.

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

lazy val root = (project in file("."))
  .settings(
    name := "%NAME%",
    libraryDependencies ++= Seq(
      "edu.berkeley.cs" %% "chisel3" % "3.4.3",
      "edu.berkeley.cs" %% "chiseltest" % "0.3.3" % "test"
    ),
    scalacOptions ++= Seq(
      "-Xsource:2.11",
      "-language:reflectiveCalls",
      "-deprecation",
      "-feature",
      "-Xcheckinit",
      // Enables autoclonetype2 in 3.4.x (on by default in 3.5)
      "-P:chiselplugin:useBundlePlugin"
    ),
    addCompilerPlugin("edu.berkeley.cs" % "chisel3-plugin" % "3.4.3" cross CrossVersion.full),
    addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)
  )
  .dependsOn(difftest)

lazy val difftest = project in file("./difftest")

主要修改的部分为为root添加源码依赖路径~

@lsyic
Copy link

lsyic commented Jul 27, 2021

请问使用difftest框架之后,可以查看仿真波形吗?怎么查看呢?

@huxuan0307
Copy link

NEMU 编译的时候需要但不限于以下几个库,如果是wsl Ubuntu20.04。按以下命令安排即可:

sudo apt install bison libncursesw5-dev libreadline-dev

@vx-zzy
Copy link

vx-zzy commented Aug 2, 2021

指令提交连线正确但是无法提交

这两天有空就在弄 Difftest,跟着两位的步骤走,自认为连线正确但是就是无法出现The first instruction of core 0 has commited. Difftest enabled.。今天阅读 Difftest 的源码才发现,是因为 pc 的值应该从 0x80000000 开始,而不是 0。把 pc 的初始值改了就好了。不知道还有没有小伙伴遇到跟我一样的问题…

参考 Difftest 的源码:

https://github.com/OpenXiangShan/difftest/blob/c8711873af7ddec278013c44166a95b728aa8a48/src/test/csrc/difftest/difftest.cpp#L273-L286

我把PC地址从0改回800000000之后,报错:

Simulating...
Emu compiled at Aug  1 2021, 20:27:52
The image is add-riscv64-mycpu.bin
Using simulated 256MB RAM
dump wave to /home/zzy/ysyx/oscpu/projects/rv64i/build/2021-08-01@20:27:52.vcd...
[warning] sdcard img not found
--diff is not given, try to use $(NEMU_HOME)/build/riscv64-nemu-interpreter-so by default
Using /home/zzy/ysyx/oscpu/NEMU/build/riscv64-nemu-interpreter-so for difftest
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'clint' at [0xa2000000, 0xa200ffff]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'sdhci' at [0xa3000000, 0xa300007f]
[src/device/sdcard.c,118,init_sdcard] Can not find sdcard image: /home/yzh/projectn/debian.img
ERROR: ram wIdx = 0x1ffffffff0000000 out of bound!
emu: /home/zzy/ysyx/oscpu/difftest/src/test/csrc/common/ram.cpp:207: void ram_write_helper(uint64_t, uint64_t, uint64_t, uint8_t): Assertion `wIdx < EMU_RAM_SIZE / sizeof(uint64_t)' failed.
./build.sh: line 193: 21811 Aborted                 (core dumped) ./$EMU_FILE $PARAMETERS
Failed to simulate!!!

GTKWave Analyzer v3.3.103 (w)1999-2019 BSI

请问老哥知道这个怎么解决吗,谢谢

@SingularityKChen
Copy link

指令提交连线正确但是无法提交

这两天有空就在弄 Difftest,跟着两位的步骤走,自认为连线正确但是就是无法出现The first instruction of core 0 has commited. Difftest enabled.。今天阅读 Difftest 的源码才发现,是因为 pc 的值应该从 0x80000000 开始,而不是 0。把 pc 的初始值改了就好了。不知道还有没有小伙伴遇到跟我一样的问题…
参考 Difftest 的源码:
https://github.com/OpenXiangShan/difftest/blob/c8711873af7ddec278013c44166a95b728aa8a48/src/test/csrc/difftest/difftest.cpp#L273-L286

我把PC地址从0改回800000000之后,报错:

Simulating...
Emu compiled at Aug  1 2021, 20:27:52
The image is add-riscv64-mycpu.bin
Using simulated 256MB RAM
dump wave to /home/zzy/ysyx/oscpu/projects/rv64i/build/2021-08-01@20:27:52.vcd...
[warning] sdcard img not found
--diff is not given, try to use $(NEMU_HOME)/build/riscv64-nemu-interpreter-so by default
Using /home/zzy/ysyx/oscpu/NEMU/build/riscv64-nemu-interpreter-so for difftest
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'clint' at [0xa2000000, 0xa200ffff]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'sdhci' at [0xa3000000, 0xa300007f]
[src/device/sdcard.c,118,init_sdcard] Can not find sdcard image: /home/yzh/projectn/debian.img
ERROR: ram wIdx = 0x1ffffffff0000000 out of bound!
emu: /home/zzy/ysyx/oscpu/difftest/src/test/csrc/common/ram.cpp:207: void ram_write_helper(uint64_t, uint64_t, uint64_t, uint8_t): Assertion `wIdx < EMU_RAM_SIZE / sizeof(uint64_t)' failed.
./build.sh: line 193: 21811 Aborted                 (core dumped) ./$EMU_FILE $PARAMETERS
Failed to simulate!!!

GTKWave Analyzer v3.3.103 (w)1999-2019 BSI

请问老哥知道这个怎么解决吗,谢谢

这个看样子是地址超出了边界。你得看看你 pc 输入到 ramhelper 的地址有没有按照 demo 那样减去 pc start 再处理。

@vx-zzy
Copy link

vx-zzy commented Aug 2, 2021

指令提交连线正确但是无法提交

这两天有空就在弄 Difftest,跟着两位的步骤走,自认为连线正确但是就是无法出现The first instruction of core 0 has commited. Difftest enabled.。今天阅读 Difftest 的源码才发现,是因为 pc 的值应该从 0x80000000 开始,而不是 0。把 pc 的初始值改了就好了。不知道还有没有小伙伴遇到跟我一样的问题…
参考 Difftest 的源码:
https://github.com/OpenXiangShan/difftest/blob/c8711873af7ddec278013c44166a95b728aa8a48/src/test/csrc/difftest/difftest.cpp#L273-L286

我把PC地址从0改回800000000之后,报错:

Simulating...
Emu compiled at Aug  1 2021, 20:27:52
The image is add-riscv64-mycpu.bin
Using simulated 256MB RAM
dump wave to /home/zzy/ysyx/oscpu/projects/rv64i/build/2021-08-01@20:27:52.vcd...
[warning] sdcard img not found
--diff is not given, try to use $(NEMU_HOME)/build/riscv64-nemu-interpreter-so by default
Using /home/zzy/ysyx/oscpu/NEMU/build/riscv64-nemu-interpreter-so for difftest
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'clint' at [0xa2000000, 0xa200ffff]
[src/device/io/mmio.c,13,add_mmio_map] Add mmio map 'sdhci' at [0xa3000000, 0xa300007f]
[src/device/sdcard.c,118,init_sdcard] Can not find sdcard image: /home/yzh/projectn/debian.img
ERROR: ram wIdx = 0x1ffffffff0000000 out of bound!
emu: /home/zzy/ysyx/oscpu/difftest/src/test/csrc/common/ram.cpp:207: void ram_write_helper(uint64_t, uint64_t, uint64_t, uint8_t): Assertion `wIdx < EMU_RAM_SIZE / sizeof(uint64_t)' failed.
./build.sh: line 193: 21811 Aborted                 (core dumped) ./$EMU_FILE $PARAMETERS
Failed to simulate!!!

GTKWave Analyzer v3.3.103 (w)1999-2019 BSI

请问老哥知道这个怎么解决吗,谢谢

这个看样子是地址超出了边界。你得看看你 pc 输入到 ramhelper 的地址有没有按照 demo 那样减去 pc start 再处理。

L%VY~2C@)FJR08KWK`Y{_ M
PC已经减去了,感觉并不是输入部分PC的问题

@lsyic
Copy link

lsyic commented Aug 15, 2021

请问用什么命令编译所有的cpu_test的程序?

@ColsonZhang
Copy link

请问用什么命令编译所有的cpu_test的程序?

应该只能一个一个编译吧。可以用下面的命令编译($FILENAME是cpu_test的文件名,例如add)。

make ARCH=riscv64-mycpu ALL=$FILENAME

@lsyic
Copy link

lsyic commented Aug 15, 2021

make ARCH=riscv64-mycpu ALL=$FILENAME

多谢多谢,已经找到了。直接运行make ARCH=riscv64-mycpu 就是编译所有的!

@youonlylooktwice
Copy link

你好!请问有人遇到过这样的问题吗?
就在mill -i __.test.runMain top.TopMain -td ./build这条命令运行
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
#Difftest Difftest 相关问题 知识分享 该issue的主题是信息、知识分享
Projects
None yet
Development

No branches or pull requests