「BUAA CO」计组小白的自动化测试

「BUAA CO」计组小白的自动化测试

Squirrel7ang Lv2

关于iverilog和verilog的自动化测试

从P4开始的CPU搭建全部是使用Verilog语言来实现的,使用iverilog进行轻量自动化测试还是很方便的。需要注意的是,考试用的虚拟机有pythongcc,但是没有iverilog vvp gtkwavewindows中也没有。

下面以P4为例,简单介绍一下自动化测试的方式,以避免像我这样的计组小白走弯路。下面使用的是22级计组官方debian虚拟机进行自动化测试。windows与之类似。

假设写好的测试文件名为test.asm

但如果你是一个希望探索计组的小白,那还是建议自己搭建自动化测评装置。

用MARS输出标准答案

标准MARS无法使用命令行在运行过程中对寄存器或者内存进行监视,建议自行寻找解决方案,比如自己魔改MARS或者写一个自动化仿真程序

Folding 但是如果改不了一点...

可以使用Toby学长改好的MARS,链接

将下载的文件重命名为mars.jar,并放在和test.asm文件相同的文件夹下,在该目录的终端下运行:

1
$ java -jar mars.jar mc CompactLargeText coL1 test.asm > mars_tmp.txt

这会将mars运行test.asm的正确结果连通mars的输出信息重定向写入mars_tmp.txt文件中。文件除了包含正确输出以外,还包含Mars的输出信息。因此需要在后续步骤中对格式进行检查。这我们在后面再说。

用MARS输出机器码

标准Mars、魔改Mars和课程组官方Mars都能实现这一点,只需要运行

1
$ java -jar mars.jar dump .text HexText code.txt mc CompactLargeText test.asm

就会在本地生成一个code.txt文件

用iverilog对CPU进行仿真

下面是关于如何实现在有正确输出结果的情况下,将CPU的输出结果输出到文本文件中。

1. 安装iverilog和vvp

打开Debian终端,或者在Debian虚拟机中按下Win+R键,在其中输入:

1
$ sudo apt-get install iverilog

这会安装iverilog以及vvpiverilog是用于对verilog进行仿真的工具,其中iverilog能够对verilog进行编译,而vvp能够对编译出来的结果进行仿真。可以理解为前者编译后者运行。

2. 编写testbench

在testbench的initial语句块的结束写上# [仿真时长] $finish;告诉iverilog要仿真多久结束。和ISE不同,ISE的仿真似乎默认1000周期,但是iverilog默认一直仿真。用$stop替代$finish相当于断点的效果,在仿真结束后vvp会进入interact模式而非直接退出。

Ctrl+C可以停止仿真。

1
2
3
4
5
6
initial begin
// dumpfile("wave.vcd") // 用于gtkwave示波
// dumpvars(0, mips_tb) // 同上
//...
# 4000 $finish;
end

testbench我这里就不写了,毕竟不同人的写法不同,而且单周期的testbench基本上能跑起来就没问题,不需要考虑什么时钟周期乱七八糟的。

3. 用iverilog运行代码

假设mips_tb.v中的模块为mips_tb。其中包含了mips模块

1
2
$ iverilog -s mips_tb -o mips_tb.out *.v
$ vvp mips_tb.out

-o表示将编译结果输出到当前目录下的mips_tb.out文件中,-s mips_tb指定仿真的顶层模块名。iverilog默认将没有被实例化的模块设置为root模块,这意味着如果有多个testbench文件的话,iverilog会一起进行仿真。在P7中我们可能会利用不同的testbench进行不同的中断测试,当我们想要使用特定的testbench进行仿真的时候就会需要使用到-s参数。只有1个testbench的时候可以不加-s参数。

4. 重定向输出

vvp进行仿真之后会将CPUiverilog的信息一同输出到标准输出中,我们将这些输出重定向到文件tmp.txt中。这个文件中不仅包含了CPU的输出,也包含了iverilog的仿真信息。我们先将其输出到一个临时文件中。

1
$ vvp mips_tb.out > my_tmp.txt

5. 检查输出格式

22级计算机组成对CPU输出格式有要求,在测评的时候也会忽略掉不符合输出格式的输出。

寄存器信息:<仿真时间>@<程序计数器地址>: ​28 <= ff00ff00。
数据存储器信息:<仿真时间>@<程序计数器地址>: *<数据存储器地址> <= <写入数据>,如 1746@00003704: *00000018 <= 69b5cca3

但是在正确性的比对中,我们不需要关心时钟周期,只需要关注@开始的部分即可。

linux中的grep指令可以方便地按照正则表达式对文本进行匹配查找。

CPU输出格式的正则表达式可以写为'(@[0-9a-f]{8}: *\*[0-9a-f]{8} *<= *[0-9a-f]{8} *\n)|(@[0-9a-f]{8}: *\$[0-9]{2} *<= *[0-9a-f]{8} *\n)'

但是为了简单起见,我们用@进行匹配就行。运行:

1
2
$ grep '@' my_tmp.txt > my.txt
$ grep '@' mars_tmp.txt > mars.txt

这样子我们就把所有含有@的行选出来输出到后面的文件中了。

比对输出文件

linux中的diff指令可以清晰地看到两个文件的差异,缺点是看到的太详细了。不过我个人不太计较。

1
$ diff -y (-H) -B -b my.txt mars.txt > result.txt

其中-y表示将文件“肩并肩”地输出。-H用于大规模文本比对,我们的规模比较小,不需要。-B表示不对空行进行比对。-b表示不对空格进行比对。我们将比较的结果重定向到了result.txt文件中。

总结

大概要用到这些指令:

1
2
3
4
5
6
7
8
9
10
11
$ java -jar mars.jar mc CompactLargeText coL1 test.asm > mars_tmp.txt

$ java -jar mars.jar dump .text HexText code.txt mc CompactLargeText test.asm

$ iverilog -s mips_tb -o mips_tb.out *.v
$ vvp mips_tb.out > my_tmp.txt

$ grep '@' my_tmp.txt > my.txt
$ grep '@' mars_tmp.txt > mars.txt

$ diff -y (-H) -B -b my.txt mars.txt > result.txt

编写成shell脚本

shell脚本在linux中能够方便的运行多条linux指令。在第二学年的春季学期我们也会接触到它的具体写法,因此我也不会。

不过我们也不需要会。

打开ChatGPT,耐(man)心(man)指(tiao)导(jiao)它帮我们写一个小shell脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/bin/bash

# 文件路径
mars_jar="mars.jar"
code_txt="code.txt"
verilog_files="*.v"
mars_tmp="mars_tmp.txt"
my_tmp="my_tmp.txt"
tb_out="mips_tb.out"

# 存储临时文件名的数组
tmp_files=("$mars_tmp" "$my_tmp" "$my_tmp.txt" "$mars_tmp.txt" "$tb_out")

# 清理临时文件的函数
clean_tmp_files() {
for file in "${tmp_files[@]}"; do
if [ -f "$file" ]; then
rm "$file"
fi
done
}

# 循环处理每个test.asm文件
for asm_file in test*.asm; do
# 生成对应的result.txt文件名
result_file="${asm_file%.asm}_result.txt"

# 第一条指令
java -jar "$mars_jar" mc CompactLargeText coL1 "$asm_file" > "$mars_tmp"

# 第二条指令
java -jar "$mars_jar" dump .text HexText "$code_txt" mc CompactLargeText "$asm_file" > /dev/null 2>&1

# 第三条指令
iverilog -s mips_tb -o $tb_out $verilog_files > /dev/null 2>&1

# 第四条指令
vvp $tb_out > "$my_tmp"

# 第五条指令
grep '@' "$my_tmp" > "$my_tmp.txt"

# 第六条指令
grep '@' "$mars_tmp" > "$mars_tmp.txt"

# 第七条指令
if ! diff -q "$my_tmp.txt" "$mars_tmp.txt"; then
diff -y -H -B -b "$my_tmp.txt" "$mars_tmp.txt" > "$result_file"
echo "$asm_file 运行不正确,比对结果在 $result_file 中。"
else
echo "$asm_file 运行正确。"
fi
done

# 清理临时文件
clean_tmp_files

将之前提到的所有文件都放在同一个文件目录下。

调用指令

1
2
$ touch test.sh
$ chmod +x test.sh

这会新建一个叫test.sh的文件,然后将这个文件标记为可执行文件。

然后打开test.sh文件,将上述代码写进去。

我们运行一下:

1
2
3
4
5
$ ./test.sh
test.asm 运行正确。
$ ls
ALU.v* CTRL.v* EXT.v* header.v* Mars.jar* mips.v* test.sh*
code.txt* DM.v* GRF.v* IFU.v* mips_tb.v* test.asm*

成功。

即便不懂shell脚本,瞪几眼也大概能看懂,可读性还是很高的。可以在这个基础上修改一下,让自动化测试符合自己的预期要求。

ChatGPT

P5测试

(待补充 ~)

(概率不补充,因为自己也只会胡说八道)。

  • 标题: 「BUAA CO」计组小白的自动化测试
  • 作者: Squirrel7ang
  • 创建于 : 2024-01-09 21:40:00
  • 更新于 : 2024-01-15 00:09:01
  • 链接: https://redefine.ohevan.com/2024/01/09/CO/AutoTest/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论