Gemmini测试test文件chisel源码详解(一)

DMACommandTrackerTest.scala

源码如下:

package gemmini

import scala.collection.mutable.ArrayBuffer

import chisel3._
import chisel3.iotesters.{ChiselFlatSpec, PeekPokeTester}

class DMACommandTrackerTester(c: DMAReadCommandTracker[UInt]) extends PeekPokeTester(c) {
  case class AllocatedCmd(id: Int, tag: Int, requestsSent: Int)
  
  var max_cycles = 100000000

  var cmdsToAllocate = 100
  val cmdIdsAllocated: ArrayBuffer[AllocatedCmd] = ArrayBuffer() // TODO use a map instead

  while ((cmdsToAllocate > 0 || cmdIdsAllocated.size > 0) && max_cycles > 0) {
    val can_allocate = cmdsToAllocate > 0 && rnd.nextBoolean()
    poke(c.io.alloc.valid, can_allocate)

    val tag = rnd.nextInt(100)
    poke(c.io.alloc.bits.tag, tag)

    val can_read_cmd = rnd.nextBoolean()
    poke(c.io.cmd_completed.ready, can_read_cmd)

    if (rnd.nextBoolean() || cmdIdsAllocated.size == 0) {
      poke(c.io.request_returned.valid, false)
    } else {
      val cmd_buf_id = rnd.nextInt(cmdIdsAllocated.size)
      val id = cmdIdsAllocated(cmd_buf_id).id
      val requestsSent = cmdIdsAllocated(cmd_buf_id).requestsSent

      if (requestsSent < c.nRequests) {
        poke(c.io.request_returned.valid, true)
        poke(c.io.request_returned.bits.cmd_id, id)

        cmdIdsAllocated(cmd_buf_id) = cmdIdsAllocated(cmd_buf_id).copy(requestsSent=requestsSent+1)
      } else {
        poke(c.io.request_returned.valid, false)
      }
    }

    val alloc_fire = can_allocate && peek(c.io.alloc.ready) != 0
    if (alloc_fire) {
      cmdsToAllocate -= 1
      val cmd_id = peek(c.io.alloc.bits.cmd_id).toInt
      assert(!cmdIdsAllocated.exists(_.id == cmd_id), s"$cmd_id already allocated")
      cmdIdsAllocated += AllocatedCmd(cmd_id, tag, 0)
    }

    val cmd_completed_fire = can_read_cmd && peek(c.io.cmd_completed.valid) != 0
    if (cmd_completed_fire) {
      val cmd_id = peek(c.io.cmd_completed.bits.cmd_id).toInt
      val cmd_buf_id = cmdIdsAllocated.zipWithIndex.collectFirst { case (AllocatedCmd(id, _, _), i) if id == cmd_id => i }.get
      val tag = peek(c.io.cmd_completed.bits.tag).toInt

      if (cmdIdsAllocated(cmd_buf_id).tag != tag) {
        println(s"wrong tag, $tag, ${cmdIdsAllocated(cmd_buf_id).tag}")
        max_cycles = 0
      }

      assert(cmdIdsAllocated(cmd_buf_id).tag == tag, "tag is incorrect")
      assert(cmdIdsAllocated(cmd_buf_id).requestsSent == c.nRequests, "returned after wrong number of requests returned")
      cmdIdsAllocated.remove(cmd_buf_id)
    }

    step(1)
    max_cycles -= 1
  }

  assert(max_cycles > 0, "reached max_cycles")
}

class DMACommandTrackerUnitTest extends ChiselFlatSpec {
  val testerArgs = Array(
    "--backend-name", "treadle",
    // "--generate-vcd-output", "on",
    "--target-dir", "test_run_dir/dmacommandtracker"
  )

  behavior of "DMACommandTracker"
  it should "work" in {
    chisel3.iotesters.Driver.execute(testerArgs, () => new DMAReadCommandTracker(2, 16, UInt(29.W))) {
      c => new DMACommandTrackerTester(c)
    } should be (true)
  }
}

这段代码是用于测试 DMAReadCommandTracker 模块的功能和正确性。DMAReadCommandTracker 模块是一个用于跟踪 DMA 读取命令的状态的硬件模块,它可以分配和回收命令 id 和 tag,并在所有请求都返回后输出完成信号。

这段代码包含两个类,DMACommandTrackerTester 和 DMACommandTrackerUnitTest。DMACommandTrackerTester 类是一个用于对 DMAReadCommandTracker 模块进行输入输出操作和断言检查的测试类。

DMACommandTrackerUnitTest 类是一个用于运行 DMACommandTrackerTester 的测试类,它继承自 ChiselFlatSpec 类,并指定了一些测试参数,如后端名称,目标目录等。

这段代码的目的是验证 DMAReadCommandTracker 模块是否能正确地分配和回收命令 id 和 tag,并在所有请求都返回后输出完成信号。

  • DMACommandTrackerTester 类是一个继承自 PeekPokeTester 的测试器类,它接受一个 DMAReadCommandTracker 类型的参数 c。

    • AllocatedCmd 是一个 case class,它用于存储已分配命令的 id、tag 和 requestsSent,id是命令编号、tag是标签(记录版本类型等)、requestsSent记录命令的进度。
    • max_cycles 是一个整数变量,它表示最大的执行周期数,用于限制测试的时间。
    • cmdsToAllocate 是一个整数变量,它表示要分配的命令数量,初始值为 100。
    • cmdIdsAllocated 是一个 ArrayBuffer 类型的变量,它用于存储已分配命令的 AllocatedCmd 对象。
    • while 循环是测试的主要逻辑,它在 cmdsToAllocate 大于 0 或 cmdIdsAllocated 不为空,并且 max_cycles 大于 0 的条件下执行。循环中包含以下步骤:
      • 随机决定是否可以分配新的命令,并将结果赋值给 can_allocate 变量,然后将其 poke 给 c.io.alloc.valid 输入端口。
      • 随机生成一个 0 到 99 之间的整数作为 tag,并将其 poke 给 c.io.alloc.bits.tag 输入端口。
      • 随机决定是否可以读取已完成的命令,并将结果赋值给 can_read_cmd 变量,然后将其 poke 给 c.io.cmd_completed.ready 输入端口。
      • 如果随机决定不返回请求或 cmdIdsAllocated 为空,则将 false poke 给 c.io.request_returned.valid 输入端口;否则,从 cmdIdsAllocated 中随机选择一个已分配命令,获取其 id 和 requestsSent,如果 requestsSent 小于 c.nRequests,则将 true poke 给 c.io.request_returned.valid 输入端口,并将 id poke 给 c.io.request_returned.bits.cmd_id 输入端口,同时将 cmdIdsAllocated 中对应的 AllocatedCmd 对象的 requestsSent 增加 1;否则,将 false poke 给 c.io.request_returned.valid 输入端口。
      • 判断是否发生了 alloc_fire 事件,即 can_allocate 为 true 并且 c.io.alloc.ready 输出端口为 true。如果是,则将 cmdsToAllocate 减 1,并从 c.io.alloc.bits.cmd_id 输出端口读取分配的 cmd_id,断言 cmdIdsAllocated 中不存在相同的 id,并将新建的 AllocatedCmd 对象添加到 cmdIdsAllocated 中。
      • 判断是否发生了 cmd_completed_fire 事件,即 can_read_cmd 为 true 并且 c.io.cmd_completed.valid 输出端口为 true。如果是,则从 c.io.cmd_completed.bits.cmd_id 和 c.io.cmd_completed.bits.tag 输出端口读取完成的 cmd_id 和 tag,并在 cmdIdsAllocated 中找到对应的 AllocatedCmd 对象和索引。如果对象中的 tag 不等于输出端口中的 tag,则打印错误信息并将 max_cycles 设为 0;否则,断言对象中的 tag 等于输出端口中的 tag,并断言对象中的 requestsSent 等于 c.nRequests,然后从 cmdIdsAllocated 中移除该对象。
      • 调用 step(1) 方法执行一个时钟周期,并将 max_cycles 减 1。
    • 在 while 循环结束后,断言 max_cycles 大于 0,表示测试没有超时。
  • DMACommandTrackerUnitTest 类是一个继承自 ChiselFlatSpec 的单元测试类,它定义了一些变量和方法。

    • testerArgs 是一个字符串数组,它存储了一些测试参数,如后端名称、目标目录等。
    • behavior of “DMACommandTracker” 是一个字符串字面量,它表示要测试的类名。
    • it should “work” 是一个方法,它表示要测试的功能。方法中包含以下步骤:
      • 调用 chisel3.iotesters.Driver.execute 方法,传入 testerArgs 和一个匿名函数,该函数返回一个新建的 DMAReadCommandTracker 对象,其参数为 2、16 和 UInt(29.W)。
      • 在 execute 方法的第二个参数中,传入一个匿名函数,该函数接受一个 DMAReadCommandTracker 类型的参数 c,并返回一个新建的 DMACommandTrackerTester 对象,其参数为 c。
      • 在 execute 方法的返回值上调用 should be (true) 方法,断言测试结果为 true。

注释版:

package gemmini

import scala.collection.mutable.ArrayBuffer

import chisel3._
import chisel3.iotesters.{ChiselFlatSpec, PeekPokeTester}


//测试 DMAReadCommandTracker 模块的功能和正确性。
//DMAReadCommandTracker 模块是一个用于跟踪 DMA 读取命令的状态的硬件模块,它可以分配和回收命令 id 和 tag,并在所有请求都返回后输出完成信号。
//包含两个类,DMACommandTrackerTester 和 DMACommandTrackerUnitTest。
//DMACommandTrackerTester 类是一个用于对 DMAReadCommandTracker 模块进行输入输出和断言检查的测试类。
//DMACommandTrackerUnitTest 类是一个用于运行 DMACommandTrackerTester 的测试类
//这段代码的目的是验证 DMAReadCommandTracker 模块是否能正确地分配和回收命令 id 和 tag,并在所有请求都返回后输出完成信号。

class DMACommandTrackerTester(c: DMAReadCommandTracker[UInt]) extends PeekPokeTester(c) {
//用于存储已分配命令的 id、tag 和 requestsSent,id是命令编号、tag是标签(记录版本类型等)、requestsSent记录命令的进度。
  case class AllocatedCmd(id: Int, tag: Int, requestsSent: Int)
//最大的执行周期数
  var max_cycles = 100000000
//要分配的命令数量,初始值为 100;cmdIdsAllocated存储已分配命令的 AllocatedCmd 对象
  var cmdsToAllocate = 100
  val cmdIdsAllocated: ArrayBuffer[AllocatedCmd] = ArrayBuffer() // TODO use a map instead
//循环条件:1.还有剩余的周期数可执行 2.待分配命令数不是0或者已分配命令不为空
  while ((cmdsToAllocate > 0 || cmdIdsAllocated.size > 0) && max_cycles > 0) {
//当还有命令可以分配的时候,随机决定是否可以分配新的命令,并将结果赋值给 can_allocate 变量,然后将其 poke 给 c.io.alloc.valid 输入端口。
    val can_allocate = cmdsToAllocate > 0 && rnd.nextBoolean()
    poke(c.io.alloc.valid, can_allocate)
//随机生成一个 0 到 99 之间的整数作为 tag,并将其 poke 给 c.io.alloc.bits.tag 输入端口。
    val tag = rnd.nextInt(100)
    poke(c.io.alloc.bits.tag, tag)
//随机决定是否可以读取已完成的命令,并将结果赋值给 can_read_cmd 变量,然后将其 poke 给 c.io.cmd_completed.ready 输入端口。
//当 can_read_cmd 为 true 时,表示允许读取已完成的命令,即表明模块已准备好接收命令完成的信号。当 can_read_cmd 为 false 时,表示不允许读取已完成的命令,即表明模块不准备接收命令完成的信号。
    val can_read_cmd = rnd.nextBoolean()
    poke(c.io.cmd_completed.ready, can_read_cmd)
//如果随机决定不返回请求或 cmdIdsAllocated 为空,则将 false poke 给 c.io.request_returned.valid 输入端口
    if (rnd.nextBoolean() || cmdIdsAllocated.size == 0) {
      poke(c.io.request_returned.valid, false)
    } else {
//从 cmdIdsAllocated 中随机选择一个已分配命令,获取其 id 和 requestsSent
      val cmd_buf_id = rnd.nextInt(cmdIdsAllocated.size)
      val id = cmdIdsAllocated(cmd_buf_id).id
      val requestsSent = cmdIdsAllocated(cmd_buf_id).requestsSent

//nRequests 用于表示每个已分配的命令所需的请求次数。在这个上下文中,当已分配的命令的请求次数达到 nRequests 时,该命令将被认为是完成的。
//如果 requestsSent 小于 c.nRequests,则将 true poke 给 c.io.request_returned.valid 输入端口
//将 id poke 给 c.io.request_returned.bits.cmd_id 输入端口,同时将 cmdIdsAllocated 中对应的 AllocatedCmd 对象的 requestsSent 增加 1;否则,将 false poke 给 c.io.request_returned.valid 输入端口。

//说白了就是判断这个命令是否完成了,然后作相应操作
      if (requestsSent < c.nRequests) {
        poke(c.io.request_returned.valid, true)
        poke(c.io.request_returned.bits.cmd_id, id)

        cmdIdsAllocated(cmd_buf_id) = cmdIdsAllocated(cmd_buf_id).copy(requestsSent=requestsSent+1)
      } else {
        poke(c.io.request_returned.valid, false)
      }
    }
//检查是否可以分配命令,(即 can_allocate 为 true 并且 c.io.alloc.ready 输出端口为 true)
//如果是,则将 cmdsToAllocate 减 1,并从 c.io.alloc.bits.cmd_id 输出端口读取分配的 cmd_id,断言 cmdIdsAllocated 中不存在相同的 id,并将新建的 AllocatedCmd 对象添加到 cmdIdsAllocated 中。
    val alloc_fire = can_allocate && peek(c.io.alloc.ready) != 0
    if (alloc_fire) {
      cmdsToAllocate -= 1
      val cmd_id = peek(c.io.alloc.bits.cmd_id).toInt
      assert(!cmdIdsAllocated.exists(_.id == cmd_id), s"$cmd_id already allocated")
      cmdIdsAllocated += AllocatedCmd(cmd_id, tag, 0)
    }
//检查是否可以读取完成的命令,(即 can_read_cmd 为 true 且 c.io.cmd_completed.valid 输出端口的值不为 0)
//如果是,则读取id和tag,并在 cmdIdsAllocated 中找到对应的 AllocatedCmd 对象和索引。
//如果原tag与输出端口的tag不同就报错,相同就删除这个命令
    val cmd_completed_fire = can_read_cmd && peek(c.io.cmd_completed.valid) != 0
    if (cmd_completed_fire) {
      val cmd_id = peek(c.io.cmd_completed.bits.cmd_id).toInt
      val cmd_buf_id = cmdIdsAllocated.zipWithIndex.collectFirst { case (AllocatedCmd(id, _, _), i) if id == cmd_id => i }.get
      val tag = peek(c.io.cmd_completed.bits.tag).toInt

      if (cmdIdsAllocated(cmd_buf_id).tag != tag) {
        println(s"wrong tag, $tag, ${cmdIdsAllocated(cmd_buf_id).tag}")
        max_cycles = 0
      }

      assert(cmdIdsAllocated(cmd_buf_id).tag == tag, "tag is incorrect")
      assert(cmdIdsAllocated(cmd_buf_id).requestsSent == c.nRequests, "returned after wrong number of requests returned")
      cmdIdsAllocated.remove(cmd_buf_id)
    }
//每次循环周期减少
    step(1)
    max_cycles -= 1
  }
//判断有没有超市
  assert(max_cycles > 0, "reached max_cycles")
}

class DMACommandTrackerUnitTest extends ChiselFlatSpec {
//存取测试参数
  val testerArgs = Array(
    "--backend-name", "treadle",
    // "--generate-vcd-output", "on",
    "--target-dir", "test_run_dir/dmacommandtracker"
  )
//表示要测试的是 DMACommandTracker 模块
  behavior of "DMACommandTracker"
  it should "work" in {
    chisel3.iotesters.Driver.execute(testerArgs, () => new DMAReadCommandTracker(2, 16, UInt(29.W))) {
      c => new DMACommandTrackerTester(c)
    } should be (true)//测试参数与断言测试结果
  }
}

你可能感兴趣的:(计算机体系结构学习笔记,risc-v,scala,体系结构,系统架构)