测试系统开发:统一语言与脚本指南
在系统开发中,测试是至关重要的环节,但在计算机科学教育里往往未得到足够重视。我们非常重视测试,认为在开发新的硬件或软件模块 P 之前,必须先开发用于测试它的模块 T,且 T 应作为 P 开发合同的一部分。为所有指定的芯片或软件系统都提供了官方测试程序。
为了简化分散在各个项目中的众多测试的定义和执行,设计了一种统一的测试描述语言。该语言在相关工具中使用方式基本相同,包括用于模拟和测试用 HDL 编写的芯片的硬件模拟器、用于模拟和测试机器语言程序的 CPU 模拟器,以及用于模拟和测试用 VM 语言编写的程序(通常是编译后的 Jack 程序)的 VM 模拟器。
每个模拟器都有图形用户界面(GUI),可使用测试脚本以交互或批量方式测试加载的芯片或程序。测试脚本是一系列命令,将硬件或软件模块加载到相关模拟器中,并使其经历一系列预先计划的测试场景。此外,测试脚本还具备打印测试结果并与预期结果进行比较的命令。测试脚本能对底层代码进行系统、可重复且有记录的测试,这在任何硬件或软件开发项目中都是非常重要的。
不期望学习者编写测试脚本,项目材料中提供了测试所有硬件和软件模块所需的脚本。本指南旨在帮助理解这些脚本的语法和逻辑,当然也欢迎自定义和创建新脚本。
1. 通用指南
以下使用指南适用于所有软件工具和测试脚本:
-
文件格式和使用
:测试硬件或软件模块涉及四种类型的文件,建议这四个文件使用相同的前缀(文件名):
-
Xxx.yyy
:
Xxx
是被测试模块的名称,
yyy
可以是
hdl
、
hack
、
asm
或
vm
,分别代表用 HDL 编写的芯片定义、用 Hack 机器语言编写的程序、用 Hack 汇编语言编写的程序或用 VM 虚拟机语言编写的程序。
-
Xxx.tst
:测试脚本,引导模拟器执行一系列步骤来测试存储在
Xxx
中的代码。
-
Xxx.out
:可选的输出文件,脚本命令可在模拟过程中将选定变量的当前值写入该文件。
-
Xxx.cmp
:可选的比较文件,包含选定变量的预期值,即如果模块实现正确,模拟应生成的值。
所有这些文件应保存在同一文件夹中,可方便地命名为
Xxx
。在所有模拟器的文档和描述中,“当前文件夹”指的是在模拟器环境中最后打开文件的文件夹。
-
空白字符
:测试脚本(
Xxx.tst文件)中的空格、换行符和注释会被忽略。测试脚本不区分大小写,但文件名和文件夹名除外。 -
使用方法
:为每个硬件或软件模块
Xxx提供了脚本文件Xxx.tst和比较文件Xxx.cmp,用于测试对Xxx的实现。在某些情况下,还会提供Xxx的框架版本,例如缺少实现的 HDL 接口。所有项目中的文件都是纯文本文件,应使用纯文本编辑器查看和编辑。
通常,通过将提供的
Xxx.tst
脚本文件加载到相关模拟器中来开始模拟会话。脚本中的第一个命令通常加载被测试模块
Xxx
中存储的代码,接下来可选择初始化输出文件并指定比较文件,其余命令则执行实际测试。
-
模拟控制
:每个模拟器都有一组菜单和图标来控制模拟:
-
文件菜单
:允许将相关程序(
.hdl
文件、
.hack
文件、
.asm
文件、
.vm
文件或文件夹名)或测试脚本(
.tst
文件)加载到模拟器中。如果用户未加载测试脚本,模拟器将加载默认测试脚本。
-
播放图标
:指示模拟器执行当前加载的测试脚本中的下一个模拟步骤。
-
暂停图标
:指示模拟器暂停当前加载的测试脚本的执行,便于检查模拟环境的各个元素。
-
快进图标
:指示模拟器执行当前加载的测试脚本中的所有命令。
-
停止图标
:指示模拟器停止当前加载的测试脚本的执行。
-
倒带图标
:指示模拟器重置当前加载的测试脚本的执行,准备从第一个命令开始执行。
需要注意的是,上述模拟器图标并非直接“运行代码”,而是运行测试脚本,由测试脚本运行代码。
2. 在硬件模拟器上测试芯片
提供的硬件模拟器用于测试和模拟用硬件描述语言(HDL)编写的芯片定义。测试脚本通常以一些设置命令开始,随后是一系列模拟步骤,每个步骤以分号结尾。模拟步骤通常指示模拟器将芯片的输入引脚绑定到测试值,评估芯片逻辑,并将选定变量的值写入指定的输出文件。
例如,
Eq3
芯片用于检查三个 1 位输入是否相等,对其进行详尽测试需要八种测试场景。由于详尽测试的规模会随输入大小呈指数增长,因此大多数测试脚本只测试一部分有代表性的输入值。
2.1 数据类型和变量
测试脚本支持两种数据类型:整数和字符串。
-
整数常量
:可以用十进制(
%D
前缀)、二进制(
%B
前缀)或十六进制(
%X
前缀)格式表示,这些值会被转换为等效的二进制补码值。
-
字符串值
:使用
%S
前缀指定,必须用双引号括起来,仅用于打印目的,不能赋值给变量。
硬件模拟器的两相时钟(仅在测试顺序芯片时使用)会发出一系列值,时钟周期(也称为时间单位)的进展可以通过
tick
和
tock
两个脚本命令控制。
tick
将时钟值从
t
移动到
t+1/2
,
tock
从
t+1/2
移动到
t+1
,进入下一个时间单位。当前时间单位存储在只读系统变量
time
中。
脚本命令可以访问三种类型的变量:
-
引脚
:模拟芯片的输入、输出和内部引脚,例如
set in 0
命令将名为
in
的引脚的值设置为 0。
-
内置芯片的变量
:由芯片的外部实现暴露。
-
系统变量
time
:自模拟开始以来经过的时间单位数。
2.2 脚本命令
脚本是一系列命令,每个命令以逗号、分号或感叹号结尾,这些终止符具有以下语义:
-
逗号(,)
:终止一个脚本命令。
-
分号(;)
:终止一个脚本命令和一个模拟步骤。模拟步骤由一个或多个脚本命令组成。当用户使用模拟器菜单或播放图标进行单步操作时,模拟器从当前命令开始执行脚本,直到遇到分号,此时模拟暂停。
-
感叹号(!)
:终止一个脚本命令并停止脚本执行,用户可以稍后从该点继续执行,通常用于调试目的。
脚本命令分为两类:
-
设置命令
:用于加载文件和初始化设置。
| 命令 | 说明 |
| — | — |
|
load Xxx.hdl
| 将存储在
Xxx.hdl
中的 HDL 程序加载到模拟器中,文件名必须包含
.hdl
扩展名,且不能包含路径说明。模拟器将尝试从当前文件夹加载文件,如果失败则从
tools/builtInChips
文件夹加载。 |
|
output-file Xxx.out
| 指示模拟器将输出命令的结果写入指定的文件,该文件必须包含
.out
扩展名,输出文件将在当前文件夹中创建。 |
|
output-list v1, v2, …
| 指定在脚本中遇到
output
命令时要写入输出文件的内容(直到下一个
output-list
命令,如果有的话)。列表中的每个值是变量名后跟一个格式说明。该命令还会生成一个包含变量名的标题行,写入输出文件。 |
|
compare-to Xxx.cmp
| 指定每个后续
output
命令生成的输出行应与指定比较文件(必须包含
.cmp
扩展名)中的对应行进行比较。如果两行不同,模拟器将显示错误消息并停止脚本执行。比较文件应位于当前文件夹中。 |
-
模拟命令
:用于引导模拟器执行实际测试。
| 命令 | 说明 |
| — | — |
|
set varName value
| 将值赋给变量,变量可以是模拟芯片的引脚、内部变量或其芯片部件之一。值和变量的位宽必须兼容。 |
|
eval
| 指示模拟器将芯片逻辑应用于输入引脚的当前值,并计算输出值。 |
|
output
| 使模拟器执行以下逻辑:1. 获取最后一个
output-list
命令中列出的所有变量的当前值;2. 使用最后一个
output-list
命令中指定的格式创建输出行;3. 将输出行写入输出文件;4. (如果之前使用
compare-to
命令声明了比较文件)如果输出行与比较文件的当前行不同,显示错误消息并停止脚本执行;5. 推进输出文件和比较文件的行游标。 |
|
tick
| 结束当前时间单位(时钟周期)的第一阶段。 |
|
tock
| 结束当前时间单位的第二阶段,并进入下一个时间单位的第一阶段。 |
|
repeat n {commands}
| 指示模拟器重复花括号内的命令
n
次。如果省略
n
,模拟器将重复命令,直到模拟因某种原因停止(例如用户点击停止图标)。 |
|
while booleanCondition {commands}
| 指示模拟器只要布尔条件为真,就重复花括号内的命令。条件的形式为
x op y
,其中
x
和
y
可以是常量或变量名,
op
可以是
=
、
>
、
<
、
>=
、
<=
或
< >
。如果
x
和
y
是字符串,
op
可以是
=
或
< >
。 |
|
echo text
| 在模拟器状态行显示文本,文本必须用双引号括起来。 |
|
clear-echo
| 清除模拟器的状态行。 |
|
breakpoint varName value
| 在执行每个后续脚本命令后,开始将指定变量的当前值与指定值进行比较。如果变量包含指定值,执行将暂停并显示消息,否则执行正常继续,用于调试目的。 |
|
clear-breakpoints
| 清除所有先前定义的断点。 |
|
builtInChipName method argument (s)
| 使用提供的参数执行指定内置芯片部件的指定方法,内置芯片的设计者可以提供允许用户(或测试脚本)操作模拟芯片的方法。 |
内置芯片可以通过 HDL 程序或外部提供的可执行模块实现。如果是后者,芯片被称为内置芯片。内置芯片可以使用
chipName[varName]
语法访问芯片状态,其中
varName
是特定于实现的变量,应在芯片 API 中记录。例如,
set RAM16K[1017] 15
命令将
RAM16K
芯片的内存位置 1017 设置为 15。如果内置芯片维护单值内部状态,可以使用
chipName[]
访问当前状态值;如果内部状态是向量,则使用
chipName[i]
。
以测试 Hack 计算机的顶级
Computer
芯片为例,一种测试方法是将机器语言程序加载到其中,并在计算机逐指令执行程序时监控选定的值。例如,编写了一个计算
RAM[0]
和
RAM[1]
最大值并将结果写入
RAM[2]
的机器语言程序
Max.hack
。为了使用该程序测试
Computer
芯片,编写了测试脚本
ComputerMax.tst
,该脚本将
Computer.hdl
加载到硬件模拟器中,然后将
Max.hack
程序加载到其
ROM32K
芯片部件中。通过在
RAM[0]
和
RAM[1]
中放入一些值,重置计算机,运行足够的时钟周期,然后检查
RAM[2]
来判断芯片是否正常工作。
每个模拟器都有默认测试脚本,如果用户未加载测试脚本,将使用默认脚本。硬件模拟器的默认测试脚本定义如下:
graph LR
A[开始] --> B[加载默认测试脚本]
B --> C[执行脚本命令]
C --> D{是否完成}
D -- 是 --> E[结束]
D -- 否 --> C
3. 在 CPU 模拟器上测试机器语言程序
与通用的硬件模拟器不同,提供的 CPU 模拟器是专门用于模拟在 Hack 计算机上执行机器语言程序的工具。程序可以用符号或二进制 Hack 机器语言编写。
模拟涉及四个文件:被测试的程序(
Xxx.asm
或
Xxx.hack
)、测试脚本(
Xxx.tst
)、可选的输出文件(
Xxx.out
)和可选的比较文件(
Xxx.cmp
),这些文件应位于同一文件夹中,通常命名为
Xxx
。
例如,对于乘法程序
Mult.hack
,可以通过在
RAM[0]
和
RAM[1]
中放入一些值,运行程序,然后检查
RAM[2]
来测试该程序。
脚本命令可以访问 Hack 计算机的以下元素:
CPU 模拟器支持大部分在硬件模拟器中描述的命令,但有以下变化:
| 命令 | 说明 |
| — | — |
|
load progName
|
progName
可以是
Xxx.asm
或
Xxx.hack
,该命令将机器语言程序加载到模拟的指令内存中。如果程序是用汇编语言编写的,模拟器在执行加载命令时会将其即时转换为二进制。 |
|
eval
| 在 CPU 模拟器中不适用。 |
|
builtInChipName method argument (s)
| 在 CPU 模拟器中不适用。 |
|
tickTock
| 用于代替
tick
和
tock
,每次调用将时钟推进一个时间单位(周期)。 |
CPU 模拟器也有默认测试脚本。
4. 在 VM 模拟器上测试 VM 程序
提供的 VM 模拟器是用 Java 实现的虚拟机,可用于模拟 VM 程序的执行、可视化其操作并显示受影响的虚拟内存段的状态。
VM 程序由一个或多个
.vm
文件组成,模拟 VM 程序涉及被测试的程序(单个
Xxx.vm
文件或包含一个或多个
.vm
文件的
Xxx
文件夹),以及可选的测试脚本(
Xxx.tst
)、比较文件(
Xxx.cmp
)和输出文件(
Xxx.out
),这些文件应位于同一文件夹中,通常命名为
Xxx
。
VM 命令
push
和
pop
用于操作虚拟内存段(如参数、局部变量等),这些段必须分配给主机 RAM,VM 模拟器在模拟
call
、
function
和
return
命令执行时会完成此任务。
当 VM 翻译器翻译 VM 程序时,会生成将栈指针设置为 256 并调用
Sys.init
函数的机器语言代码,该函数会初始化操作系统类并调用
Main.main
。类似地,当 VM 模拟器被指示执行 VM 程序时,会从
Sys.init
函数开始运行。如果加载的 VM 代码中没有该函数,模拟器将从加载的 VM 代码的第一个命令开始执行。
例如,
FibonacciSeries.vm
文件包含一系列计算斐波那契数列前
n
个元素的 VM 命令,测试脚本使用参数 6 和 4000 测试该程序。
脚本命令可以访问虚拟机的以下元素:
-
VM 段的内容
-
VM 段的指针
-
特定于实现的变量
VM 模拟器支持大部分在硬件模拟器中描述的命令,但有以下变化:
| 命令 | 说明 |
| — | — |
|
load source
| 可选的
source
参数可以是
Xxx.vm
文件或包含一个或多个
.vm
文件的
Xxx
文件夹(此时将依次加载所有文件)。如果
.vm
文件位于当前文件夹中,可以省略
source
参数。 |
|
tick / tock
| 在 VM 模拟器中不适用。 |
|
vmstep
| 模拟单个 VM 命令的执行,并前进到代码中的下一个命令。 |
VM 模拟器也有默认脚本。
通过以上介绍,我们了解了在不同模拟器上进行硬件和软件模块测试的方法和相关脚本命令,这些测试方法和工具对于确保系统的正确性和稳定性至关重要。在实际开发中,可以根据具体需求选择合适的模拟器和测试脚本进行测试。
测试系统开发:统一语言与脚本指南(续)
5. 测试流程总结与对比
为了更清晰地理解在不同模拟器上进行测试的过程,下面对硬件模拟器、CPU 模拟器和 VM 模拟器的测试流程和命令差异进行总结对比。
| 模拟器类型 | 涉及文件 | 主要命令差异 | 测试流程要点 |
|---|---|---|---|
| 硬件模拟器 |
Xxx.hdl
、
Xxx.tst
、
Xxx.out
、
Xxx.cmp
|
支持
eval
、
tick
、
tock
、
builtInChipName method argument (s)
等命令
|
1. 加载
Xxx.hdl
文件;2. 初始化输出文件和比较文件;3. 执行模拟步骤,绑定输入引脚值,评估芯片逻辑并输出结果;4. 利用
tick
和
tock
控制时钟周期
|
| CPU 模拟器 |
Xxx.asm
或
Xxx.hack
、
Xxx.tst
、
Xxx.out
、
Xxx.cmp
|
不支持
eval
和
builtInChipName method argument (s)
,使用
tickTock
代替
tick
和
tock
|
1. 加载机器语言程序(
Xxx.asm
或
Xxx.hack
);2. 执行模拟,通过
tickTock
推进时钟;3. 监控程序执行过程中的相关值
|
| VM 模拟器 |
Xxx.vm
或包含
.vm
文件的文件夹、
Xxx.tst
、
Xxx.out
、
Xxx.cmp
|
不支持
tick
和
tock
,使用
vmstep
模拟单个 VM 命令执行
|
1. 加载 VM 程序;2. 初始化虚拟内存段;3. 执行
vmstep
逐步执行 VM 命令;4. 监控虚拟内存段状态
|
通过这个表格,可以直观地看到不同模拟器在测试时的区别,在实际测试中可以根据具体的模块类型和需求选择合适的模拟器和测试方法。
6. 测试脚本的高级应用与技巧
在实际测试过程中,除了掌握基本的测试脚本命令和流程外,还可以运用一些高级应用和技巧来提高测试效率和准确性。
6.1 脚本的自定义与扩展
虽然提供了默认的测试脚本,但在实际开发中,可能需要根据具体需求对脚本进行自定义和扩展。例如,可以在脚本中添加自定义的
echo
命令,用于在关键步骤输出提示信息,方便调试和监控测试过程。
echo "Starting the test for the Computer chip..."
load Computer.hdl
echo "Computer.hdl loaded successfully."
output-file Computer.out
compare-to Computer.cmp
echo "Output file and compare file configured."
在这个示例中,通过添加
echo
命令,在关键步骤输出提示信息,使测试过程更加清晰可见。
6.2 循环与条件语句的灵活运用
repeat
和
while
循环以及条件语句可以帮助我们更高效地进行测试。例如,在测试一个需要多次执行相同操作的芯片时,可以使用
repeat
命令。
repeat 10 {
set in 1
eval
output
}
这个脚本会将输入引脚
in
设置为 1,评估芯片逻辑并输出结果,重复执行 10 次。
而
while
语句可以根据条件动态控制测试过程。例如,在测试一个计数器芯片时,可以使用
while
语句监控计数器的值,直到达到某个条件才停止测试。
while Counter < 100 {
tick
tock
output
}
6.3 断点调试技巧
breakpoint
命令是调试测试脚本的重要工具。在测试复杂的芯片或程序时,可以设置断点,当某个变量达到特定值时暂停执行,方便检查模拟环境的状态。
breakpoint RAM[10] 255
repeat 200 {
tick
tock
output
}
在这个示例中,设置了一个断点,当
RAM[10]
的值达到 255 时,测试脚本将暂停执行,此时可以检查其他相关变量的值,找出可能存在的问题。
7. 测试的注意事项与常见问题解决
在进行测试的过程中,可能会遇到一些问题,下面列举一些注意事项和常见问题的解决方法。
7.1 注意事项
-
文件路径与命名
:所有相关文件(如
Xxx.hdl、Xxx.tst、Xxx.out、Xxx.cmp)应放在同一文件夹中,且文件名要严格按照规定的格式命名,否则可能导致文件加载失败。 -
数据类型与位宽匹配
:在使用
set命令赋值时,要确保值和变量的位宽兼容,否则可能会出现数据截断或错误的结果。 -
时钟周期控制
:在使用
tick和tock或tickTock命令时,要根据芯片或程序的特性合理控制时钟周期,避免出现时序错误。
7.2 常见问题解决
-
文件加载失败
:如果模拟器无法加载文件,首先检查文件路径是否正确,文件是否存在于当前文件夹中。如果是硬件模拟器,还可以检查
tools/builtInChips文件夹中是否有该文件。 -
测试结果与预期不符
:当测试结果与比较文件中的预期值不一致时,首先检查测试脚本中的命令是否正确,特别是
set命令的赋值是否符合要求。还可以使用breakpoint命令进行调试,找出问题所在。 - 脚本执行异常终止 :如果脚本在执行过程中异常终止,检查脚本中的语法错误,特别是命令的终止符(逗号、分号、感叹号)是否使用正确。还可以查看模拟器的错误提示信息,根据提示进行修复。
8. 未来测试技术的展望
随着系统开发的不断发展,测试技术也在不断演进。未来,测试可能会朝着更加自动化、智能化的方向发展。
- 自动化测试框架 :可能会出现更加完善的自动化测试框架,能够自动生成测试脚本,根据系统的规格和需求进行全面的测试,减少人工编写测试脚本的工作量。
- 人工智能辅助测试 :利用人工智能技术,如机器学习和深度学习,对测试结果进行分析和预测,提前发现潜在的问题,提高测试的效率和准确性。
- 跨平台测试 :随着系统的多元化和跨平台需求的增加,测试工具和技术需要支持在不同平台上进行测试,确保系统在各种环境下都能正常运行。
虽然目前我们使用的测试方法和工具已经能够满足大部分系统开发的需求,但我们也应该关注未来测试技术的发展趋势,不断学习和应用新的测试方法,以适应不断变化的开发环境。
通过对不同模拟器测试方法、脚本命令、高级应用技巧以及未来展望的介绍,我们对系统开发中的测试环节有了更全面的了解。在实际开发中,要充分利用这些测试方法和工具,确保系统的质量和稳定性。同时,也要关注测试技术的发展,不断提升自己的测试能力。
超级会员免费看
1273

被折叠的 条评论
为什么被折叠?



