测试平台 (Test Bench)
测试平台,测试台或测试工作台, 也有称作验证平台,是用于验证设计或模型的正确性或健全性的环境。
该术语起源于电子设备,系统或器件的测试,在该测试中,工程师坐在实验室的工作台上,并配备用于测量和操作的工具,例如示波器,万用表,电烙铁,剪线钳等,以及手动进行测试和验证被测设备,系统或器件(Device Under Test, DUT)的正确性。
对于小型的数字电路设计,工程师可以使用硬件描述语言来搭建测试平台。如果遇到大型集成电路项目,由于所需的测试向量相当复杂,为了达到更高的语句、分支、条件、路径、触发、翻转覆盖率,很多情况需要使用更加先进的直接随机测试方法。硬件验证语言针对随机测试的建立和功能覆盖率的提高,则提供了专用的数据结构供工程师使用
在软件 ( Software),固件 ( Firmware) 或硬件 ( Hardware) 工程的上下文中,测试台是一种环境,在其中可以使用软件和硬件工具对正在开发的产品进行测试。
“测试台”的另一个含义是隔离的,受控的环境,与生产环境非常相似,但对公众等客户而言是隐藏的/不可见的。因此进行更改是安全的,因为不涉及最终用户。
图 1 测试平台的基本概念
一般而言,测试平台可以认为是一个拥有各种用于系统,模块和器件的测试实体设备,如信号发生器,频谱仪,示波器等的实验室, 或者是拥有这些虚拟设备的虚拟实验室;通常情况下,测试平台可以完全由实物组成 ( 比较少 ) ,如一些简单的测试,像器件的波形,速度等 , 或完全由虚拟设备 ( 软件,在很多情况下 ) , 如模拟电路中的各种验证等,还有就是实物和虚拟物都有 ( 软硬件结合,多数情况下 ) , 如用于测试的设计 ( DFT) 等;
在RTL代码编写结束后,需要对其编写Testbench完成对待测设计的例化,测试代码的封装,生成输入激励,收集输出相应,决定对错和衡量进度。
测试平台和仿真环境开发
通常,测试平台是一种HDL描述,用于在经过验证的设计之上创建一个封闭的系统。测试台由三个基本组件组成:
激励驱动程序,
监视器
检查器。
激励驱动器负责为被测器件DUV提供激励。激励可以是预定的,也可以在仿真过程中生成。激励驱动器的目的不是模仿整个相邻块的行为,而是维持与被测器件DUV的接口一致性。
监视器用于观察DUV的输入,输出和任何内部感兴趣的信号。输入和输出信号上的值必须与接口协议一致,并且如果发生任何异常,监视器将发出错误提示。 检查器可以看作是一种特殊的监视器,用于检查设计意图的功能性。传统上,设计人员手动创建功能检查器,并使用它们将设计的响应与规范进行比较。随着设计变得越来越复杂,对这种检查器的自动化开发的需求也在增加。
根据覆盖率指标,验证工程师尝试准备一组测试用例以覆盖目标功能事件。在开发这样的测试用例中,经验起着至关重要的作用。为某些特定事件创建有意义的测试用例通常在很大程度上取决于设计者的知识和对规范的解释。
考虑一个16位一键编码总线协议。为了在所有情况下实现最佳覆盖,测试用例将要求每个位轮换为1,其他位为0。在推导测试用例时,可能很难仅从设计实现的结构中观察到规律性。但是,了解协议的功能将有助于捕获每个位的规则性和相似性,从而使测试生成更容易,更高效。 枚举确定性的测试用例以覆盖所有功能是很乏味的。一种替代方法是将设计规范转换为硬件设计语言 ( HDL) 模型,以自动进行检查。这样的测试台称为自检测试台,因为不再需要检查仪器。自检测试台范式可以分为三种类型:使用黄金向量进行检查,根据参考模型进行检查以及基于事务的检查。
用黄金向量进行检查是这三种方法中使用最广泛的方法。给定覆盖率指标后,验证工程师可以在输入处搜索测试案例,然后手动或通过使用辅助程序得出相应的输出响应。输入和输出矢量的这种组合称为黄金向量。在测试台将输入向量应用于被测器件DUV之后,将捕获实际响应并将其与黄金向量进行比较。当黄金响应与实际响应之间发生不匹配时,会发现一个 ( 器件 )失效。图2显示了此方法的组件。
图 2 具有黄金向量的自检测试台。
针对参考模型的检查范式使用参考模型来捕获规范参数中的所有功能。参考模型通常使用高级编程或验证语言在更抽象的级别上实现。所有输入向量都应用于参考模型和被测器件DUV,并评估和比较它们的响应。如果比较在每个周期的末尾进行,则参考模型必须是周期精确的。检查器比较被测器件DUV和参考模型的响应,如图3所示。如果规格参数发生变化,则需要对参考模型进行相应的修改。这种修改工作通常比重现“用黄金矢量检查”范式所需的所有黄金矢量的工作量要低得多。
图 3 测试平台
测试台将程序加载到内存中。 图4中的程序通过一系列的计算来执行所有指令,并且仅在所有指令均能正常执行时才能产生正确结果 ( 被测器件通过测试 )。 具体来说,如果程序正确运行,它将把值7写入地址100,但是如果硬件有问题,则做不到这些。 这是ad hoc 测试的示例。
图4 测试程序的汇编代码和机器代码
机器代码存储在一个名为memfile.dat的十六进制文件中,该文件由测试台在模拟过程中加载。 该文件由指令的机器代码组成,每行一条指令。 以下示例给出了测试平台,顶级ARM模块和外部存储器HDL代码。 在此示例中,每个存储器保存64个字节。
下面是测试平台的一些实例和程序供参考:
HDL Example 1
Testbench
SystemVerilog
module testbench();
logic clk;
logic reset;
logic [31:0] WriteData, DataAdr;
logic MemWrite;
// instantiate device to be tested
top dut(clk, reset, WriteData, DataAdr, MemWrite);
// initialize test
initial
begin
reset <= 1; # 22; reset <= 0;
end
// generate clock to sequence tests
always
begin
clk <= 1; # 5; clk <= 0; # 5;
end
// check that 7 gets written to address 0x64
// at end of program
always @(negedge clk)
begin
if(MemWrite) begin
if(DataAdr === 100 & WriteData === 7) begin
$display(“Simulation succeeded”);
$stop;
end else if (DataAdr !== 96) begin
$display(“Simulation failed”);
$stop;
end
end
end
endmodule
VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.all; use IEEE.NUMERIC_STD_UNSIGNED.all;
entity testbench is
end;
architecture test of testbench is
component top
port(clk, reset: in STD_LOGIC;
WriteData, DatAadr: out STD_LOGIC_VECTOR(31 downto 0);
MemWrite: out STD_LOGIC);
end component;
signal WriteData, DataAdr: STD_LOGIC_VECTOR(31 downto 0);
signal clk, reset, MemWrite: STD_LOGIC;
begin
– – instantiate device to be tested
dut: top port map(clk, reset, WriteData, DataAdr, MemWrite);
– – generate clock with 10 ns period
process begin
clk <= ‘1’;
wait for 5 ns;
clk <= ‘0’;
wait for 5 ns;
end process;
– – generate reset for first two clock cycles
process begin
reset <= ‘1’;
wait for 22 ns;
reset <= ‘0’;
wait;
end process;
– – check that 7 gets written to address 0x64
– – at end of program
process (clk) begin
if (clk’event and clk = ‘0’ and MemWrite = ‘1’) then
if (to_integer(DataAdr) = 100 and
to_integer(WriteData) = 7) then
report “NO ERRORS: Simulation succeeded” severity failure;
elsif (DataAdr /= 96) then
report “Simulation failed” severity failure;
end if;
end if;
end process;
end;
HDL Example 2
Top-Level Module
SystemVerilog
module top(input logic clk, reset,
output logic [31:0] WriteData, DataAdr,
output logic MemWrite);
logic [31:0] PC, Instr, ReadData;
// instantiate processor and memories
arm arm(clk, reset, PC, Instr, MemWrite, DataAdr,
WriteData, ReadData);
imem imem(PC, Instr);
dmem dmem(clk, MemWrite, DataAdr, WriteData, ReadData);
endmodule
VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.all; use IEEE.NUMERIC_STD_UNSIGNED.all;
entity top is – – top-level design for testing
port(clk, reset: in STD_LOGIC;
WriteData, DataAdr: buffer STD_LOGIC_VECTOR(31 downto 0);
MemWrite: buffer STD_LOGIC);
end;
architecture test of top is
component arm
port(clk, reset: in STD_LOGIC;
PC: out STD_LOGIC_VECTOR(31 downto 0);
Instr: in STD_LOGIC_VECTOR(31 downto 0);
MemWrite: out STD_LOGIC;
ALUResult, WriteData: out STD_LOGIC_VECTOR(31 downto 0);
ReadData: in STD_LOGIC_VECTOR(31 downto 0));
end component;
component imem
port(a: in STD_LOGIC_VECTOR(31 downto 0);
rd: out STD_LOGIC_VECTOR(31 downto 0));
end component;
component dmem
port(clk, we: in STD_LOGIC;
a, wd: in STD_LOGIC_VECTOR(31 downto 0);
rd: out STD_LOGIC_VECTOR(31 downto 0));
end component;
signal PC, Instr,
ReadData: STD_LOGIC_VECTOR(31 downto 0);
begin
– – instantiate processor and memories
i_arm: arm port map(clk, reset, PC, Instr, MemWrite, DataAdr,
WriteData, ReadData);
i_imem: imem port map(PC, Instr);
i_dmem: dmem port map(clk, MemWrite, DataAdr,
WriteData, ReadData);
end;
HDL Example 3
Data Memory
SystemVerilog
module dmem(input logic clk, we,
input logic [31:0] a, wd,
output logic [31:0] rd);
logic [31:0] RAM[63:0];
assign rd = RAM[a[31:2]]; // word aligned
always_ff @(posedge clk)
if (we) RAM[a[31:2]] <= wd;
Endmodule
VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.all; use STD.TEXTIO.all;
use IEEE.NUMERIC_STD_UNSIGNED.all;
entity dmem is – – data memory
port(clk, we: in STD_LOGIC;
a, wd: in STD_LOGIC_VECTOR(31 downto 0);
rd: out STD_LOGIC_VECTOR(31 downto 0));
end;
architecture behave of dmem is
begin
process is
type ramtype is array (63 downto 0) of
STD_LOGIC_VECTOR(31 downto 0);
variable mem: ramtype;
begin – – read or write memory
loop
if clk’event and clk = ‘1’ then
if (we = ‘1’) then
mem(to_integer(a(7 downto 2))) := wd;
end if;
end if;
rd <= mem(to_integer(a(7 downto 2)));
wait on clk, a;
end loop;
end process;
end;
HDL Example 4
Instruction Memory
SystemVerilog
module imem(input logic [31:0] a,
output logic [31:0] rd);
logic [31:0] RAM[63:0];
initial
$readmemh(“memfile.dat”,RAM);
assign rd = RAM[a[31:2]]; // word aligned
Endmodule
VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.all; use STD.TEXTIO.all;
use IEEE.NUMERIC_STD_UNSIGNED.all;
entity imem is – – instruction memory
port(a: in STD_LOGIC_VECTOR(31 downto 0);
rd: out STD_LOGIC_VECTOR(31 downto 0));
end;
architecture behave of imem is – – instruction memory
begin
process is
file mem_file: TEXT;
variable L: line;
variable ch: character;
variable i, index, result: integer;
type ramtype is array (63 downto 0) of
STD_LOGIC_VECTOR(31 downto 0);
variable mem: ramtype;
begin
– – initialize memory from file
for i in 0 to 63 loop – – set all contents low
mem(i) := (others => ‘0’);
end loop;
index := 0;
FILE_OPEN(mem_file, “memfile.dat”, READ_MODE);
while not endfile(mem_file) loop
readline(mem_file, L);
result := 0;
for i in 1 to 8 loop
read(L, ch);
if ‘0’ <= ch and ch <= ‘9’ then
result := character’pos(ch) – character’pos(‘0’);
elsif ‘a’ <= ch and ch <= ‘f’ then
result := character’pos(ch) – character’pos(‘a’)+10;
elsif ‘A’ <= ch and ch <= ‘F’ then
result := character’pos(ch) – character’pos(‘A’)+10;
else report “Format error on line ” & integer’image(index)
severity error;
end if;
mem(index)(35-i*4 downto 32-i*4) :=
to_std_logic_vector(result,4);
end loop;
index := index + 1;
end loop;
– – read memory
loop
rd <= mem(to_integer(a(7 downto 2)));
wait on a;
end loop;
end process;
end;