P3-CPU自动化测试

Shae
助教认证

P3-CPU自动化测试

本文主要实现了样例自动生成的程序并整理了Python一键对拍的必要步骤。在样例自动生成方面,尽可能地扩大覆盖范围并消除死循环的风险。若有建议,请不吝赐教~~

1.样例

1.1.覆盖范围

1.1.1.指令

  • add,sub
    • 注意:按无符号加减法处理。因此若用样例跑MARS会出现Arithmetic Overflow的报错提示。
  • ori, lw, sw,lui,beq
  • jal

1.1.2.取值

任一样例中出现边界值的概率接近百分百。

  • 寄存器
    • 0附近
    • 32位数边界附近
    • 32位数范围内的随机数(为保证lw和sw过程时地址不超过ROM和RAM的范围,故设置得较小)
  • 无符号16位立即数(ori,lui
    • 0附近
    • 16位无符号数边界附近
    • 随机数

1.1.3.跳转方向

  • 向后跳

  • 向前跳

    beq不参与其他指令的随机生成程序,自成一个模块,以特定的频率穿插在代码中。

    设置独立的跳转区,避免死循环。

    P3自动测试生成器beq向前跳.jpg

1.2.代码结构

  1. 输出重定向
  2. 指令函数
  3. 随机指令匹配
  4. 主逻辑
    • 极端值测试
    • 跳转测试

1.3.源代码

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import random
import sys
extremeImmu16 = [0, 1, 2, 3, 65533, 65534, 65535]
sizeEIu16 = 6
def extremeRegu32(r):
lui(r,65535)
ori(r,r,extremeImmu16[random.randint(0,sizeEIu16)])
return

#0.open test.asm
sys.stdout = open("D:\BUAA\practice\logism\cpu_test\\test.asm", "w")

#1.instruction
def add(rd, rs, rt):
print("add $"+str(rd)+",$"+str(rs)+",$"+str(rt))
return
def sub(rd, rs, rt):
print("sub $"+str(rd)+",$"+str(rs)+",$"+str(rt))
return
def ori(rt, rs, imm):
print("ori $"+str(rt)+",$"+str(rs)+","+str(imm))
return
def lw(rt, base):
ori(base, 0, random.randint(1,6) * 4)
offset = random.randint(0,40) * 4
print("lw $"+str(rt)+","+str(offset)+"($"+str(base)+")")
return
def sw(base, rt):
ori(base, 0, random.randint(1,6) * 4)
offset = random.randint(0,40) * 4
print("sw $"+str(rt)+","+str(offset)+"($"+str(base)+")")
return
def lui(rt, imm):
print("lui $"+str(rt)+","+str(imm))
return

#2.generate instruction
def generate(op, rs, rt, rd):
match op:
case 0:
print("nop")
case 1:
sub(rd, rs, rt)
case 2:
imm = random.randint(0, 200)
ori(rt, rs, imm)
case 3:
lw(rt, rs)
case 4:
sw(rs, rt)
case 5:
imm = random.randint(0, 200)
lui(rt, imm)
case 6:
add(rd, rs, rt)
case _:
return
return

#3.main logic
for index in range(0,31):
imm = random.randint(3, 12)
ori(index, 0, imm)

for i in range(0, 200):
rs = random.randint(0,31)
rt = random.randint(0,31)
rd = random.randint(0,31)
op = random.randint(0,6)
isExtreme = random.randint(0,1)

if op == 2:
if isExtreme == 0:
generate(op, rs, rt, rd)
else:
extremeRegu32(rt)
extremeRegu32(rs)
ori(rt, rs, extremeImmu16[random.randint(0,sizeEIu16)])
ori(rt, 0, random.randint(3, 12))
ori(rs, 0, random.randint(3, 12))
elif op == 1 or op == 6:
if isExtreme == 0:
generate(op, rs, rt, rd)
else:
extremeRegu32(rs)
extremeRegu32(rt)
extremeRegu32(rd)
generate(op, rs, rt, rd)
ori(rs, 0, random.randint(3, 12))
ori(rt, 0, random.randint(3, 12))
ori(rd, 0, random.randint(3, 12))
elif op == 5:
if isExtreme == 0:
generate(op, rs, rt, rd)
else:
extremeRegu32(rt)
lui(rt, extremeImmu16[random.randint(0,sizeEIu16)])
ori(rt, 0, random.randint(3, 12))
else:
generate(op, rs, rt, rd)

if i % 30 == 0:
index = int(i / 30)
if index < 3:
if index != 0:
jumpZoneIndex = int(index - 1)
rt = random.randint(0,31)
rs = random.randint(0,31)
print("beq $"+str(rt)+",$"+str(rs)+","+"JumpZone"+str(jumpZoneIndex))
print("nop")
print("JumpOut"+str(jumpZoneIndex)+":")
print("jal Text"+str(index))
print("nop")
print("JumpZone"+str(index)+":")
generate(op, rs, rt, rd)
print("jal JumpOut"+str(index))
print("nop")
print("Text"+str(index)+":")
else:
if index == 3:
jumpZoneIndex = int(index - 1)
rt = random.randint(0,31)
rs = random.randint(0,31)
print("beq $"+str(rt)+",$"+str(rs)+","+"JumpZone"+str(jumpZoneIndex))
print("nop")
print("JumpOut"+str(jumpZoneIndex)+":")

randomJump = random.randint(index,index+1) if index < 6 else index
rt = random.randint(0,31)
rs = random.randint(0,31)
print("beq $"+str(rt)+",$"+str(rs)+","+"JumpOut"+str(randomJump))
print("nop")
print("JumpOut"+str(index)+":")


2.对拍

参考了网上资料,整理而得

2.1.Python调用系统命令

  • os.system(cmd):将字符串转化成命令行在服务器上运行,仅在一个子终端运行系统命令,而不获取命令执行后的返回信息。
  • os.popen(cmd):不仅执行命令而且返回执行后的信息对象,通过管道文件将结果返回。可以使用read()操作看到执行的输出。

2.2.Logisim自动化测试

2.2.1.命令行

1
java -jar logisim-generic-2.7.1.jar myCpu_m.circ -tty table > cpuRes.txt
  • 路径:

    • 命令行:若输出文件没有写明路径,会存在命令行开头的地址中。
    • 程序:若输出文件没有写明路径,会存在程序所在文件夹中。
  • 如果将command写进python文件,目标文件前面应有反斜杠

    1
      command = "java -jar logisim-generic-2.7.1.jar myCpu.circ -tty table > address\\cpuRes.txt"

2.2.2.CPU测试设置

设置halt,使程序自动停止。

  • 官方文档: Logisim_halt.jpg

  • 实现:第一计数器的值是灵活可变的。两个计数器都应设置为Stay At Value

    halt_setting.jpg

2.3.Mars命令行导出机器码

1
java -jar Mars4_5.jar test.asm nc mc CompactTextAtZero a dump .text HexText hexTextCode.txt

2.4.正则表达式匹配

显而易见,ROM数据从<a name="contents">addr/data: 12 32开始,只需在此处将机器码导入即可。

P3自动化测试正则.jpg

3.集成

利用Python的系统调用命令,实现一键样例对拍。

4.致谢

我的工作主要集中在样例生成部分,感谢助教马瑀阔学长的指点✧*。 (ˊᗜˋ*) ✧*。

update:感谢杨荣津同学的提醒。已对代码中边界值部分做出修改。


ScottMa(助教) 回复 Shae

很优秀的尝试,同学们可以尝试借助林同学对对拍部分的思路提示自行编写脚本,实现评测的自动化

0%