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。
DMACommandTrackerUnitTest 类是一个继承自 ChiselFlatSpec 的单元测试类,它定义了一些变量和方法。
注释版:
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)//测试参数与断言测试结果
}
}