IC验证典型测试向量
6.典型测试向量的设计
6.1变量初始化
在Verilog语言中,有两种方法可以初始化变量 一种是利用初始化变量,另一种就是在定义变量时直接赋值初始化。这两种初始化任务是不可综合的,主要用于仿真过程。
(1)initial初始化方式
在大多数情况下,Testbench中变量初始化的工作通过initial过程块来完成,可以产生丰富的仿真激励。
initial语句只执行一次,即在设计被开始模拟执行时开始(0时刻)直到过程结束,专门用于对输入信号进行初始化和产生特定的信号波形。一个Testbench可以包含多initial过程语句块,所有的initia|过程都同时执行需要注意的是,initial语句中的变量必须为reg类型。
例:利用initial初始化方式的测试向量产出
module counter(clk,cnt);
output [3:0] cnt;
reg clk;
reg [3:0] temp;
initial temp = 0;
initial clk = 0;
endmodule
(2)定义变量时初始化在定义变量时初始化的语法非常简单,直接用"="在变量右端赋值即可,如:
reg [7:0] cnt=8’b00000000;
就将8比特的寄存器变量cnt初始化为0。
6.2数据信号测试向量产生
数据信号的产生有两种形式:其一是初始化和产生都在单个initial块中进行;其一是初始化在initial语句中完成,而产生在always语句块中完成。前者适合不规则数据序列,并且要求长度较短;后者适合具有一定规律的数据序列,长度不限。
例:产生位宽为4的质数序列{1、2、3、5、7、11、13 },并且重复两次,其中样值间隔为4个仿真时间单位
由于该序列无明显规律,因此利用Initial语句最为合适。
`timescale 1ns/1ps
module sequence_tb;
reg [3:0] qout;
parameter sample_period = 4;
parameter queue_num = 2;
initial
begin
q_out =0;
repeat( queue_num)
begin
# sample_period q_out = 1;
# sample_period q_out = 2;
# sample_period q_out = 3;
# sample_period q out = 5;
# sample_period q_out = 7;
# sample_period q_out= 11;
# sample_period q_out = 13;
end
end
e ndmodule
6.3时钟信号测试向量产生
例:产生占空比为50%的时钟信号
(1)基于initial语句的方法
module clk1(clk);
output clk;
parameter clk_period = 10;
reg clk;
initial
begin
clk = 0;
forever #(clk_period/2) clk = ~clk;
end
endmodule
(2)基于always语句的方法
module clk2(clk);
output clk;
parameter clk_period = 10;
reg clk;
initial clk = 0;
always #(clk_period/2) clk = ~clk;
endmodule
6.4总线信号测试向量产生
总线是运算部件之间数据流通的公共通道。在RTL级描述中,总线指的是由逻辑单元、寄存器、存储器、电路输入或其它总线驱动的一个共享向量。而总线功能模型则是一种将物理的接口时序操作转化成更高抽象层次接口的总线模型,如图:
在总线中,对于每个请求端,有一个输入来选择驱动该总线所对应的请求端。选择多个请求端会产生总线冲突,根据总线的类型,冲突会产生不同的结果。当有多个请求端发出请求时,相应的操作由总线的类型决定。在Verilog测试中,总线测试信号通常是通过将片选信号,读(或者写)使能信号、地址信号以及数据信号以task任务的形式来描述,通过调用以task形式的总线信号测试向量来完成相应的总线功能 。
下面以工作频率为100MHz的AHB总线写操作为例,说明以task方式产生总线信号测试向量的方式。下图是AHB总线写操作的时序图,其中,在完成数据的写操作后将片选和写使能信号置为无效(低电平有效)。
例:产生一组具有写操作AHB总线功能模型
module bus_wr_tb;
reg clk;
reg cs;
reg wr;
reg [31:0] addr;
reg [31:0] data:
initial
begin
cs=1'b1;
wr=1'b1;
#30;
bus_wr(32'h1100008a, 32’h11113000);
bus_wr(32’h1100009a, 32'h11113001);
bus_wr(32'h110000aa, 32'h11113002);
bus_wr(32'h110000ba, 32’h11113003);
bus_wr(32'h110000ca, 32’h11113004);
addr=32’bx;
data=32’bx;
end
initial clk=1;
always #5 clk=~clk;
task bus_wr;
input [31:0] ADDR;
input [31:0] DATA;
begin
cs=1'b0;
wr=1'b0;
addr = ADDR;
data = DATA;
#30 cs=1'b1;
wr=1'b1;
end
endtask
endmodule
7.用户自定义元件模型UDP
通过UDP,可以把一块组合逻辑电路或时序逻辑电路封装在一个UDP内,并把这个UDP作为一个基本门元件来使用。需要注意的是,UDP是不能综合的,只能用于仿真。
7.1UDP的定义与调用
UDP的定义格式如下:
primitive <元件名称>(<输出端口名>,<输入端口名1 >,<输入端口名2>,...,<输入端口名n>)
输出端口类型声明(output);
输入端口类型声明(input);
输出端口寄存器变量说明(reg);
元件初始状态说明(initial);
table
<table表项1>;
<table表项2>;
...
<table表项n>;
endtable
endprimitive
和Verilog中的模块相比,UDP具备以下特点。
(1)UDP的输出端口只能有一个,且必须位于端口列表的第一项。只有输出端口能定义为reg类型。
(2)UDP的输入端可有多个,一般时序电路UDP的输入端口最多9个,组合电路UDP 的输入端口可多至10个。
(3)所有端口变量的位宽必须是1比特。
(4)在table表项中,只能出现0、1、x这三种状态,z将被认为是x状态。
根据UDP包含的基本逻辑功能,可以将UDP分为组合电路UDP、时序电路UDP及混合电路UDP,这几类UDP的差别主要体现在table表项的描述上。
UDP的调用和Verilog中模块的调用方法相似,通过位置映射,其语法格式如下:
UDP名 例化名(连接端口1信号名,连接端口2信号名,连接端口3信号名,...);
例:用UDP方式定义一个全加器仿真模型
primitive summ(sum, cin, a, b);//本位和
output sum;
input a,b,cin;
table
//cin a b : sum
0 0 0 : 0;
0 0 1 : 1;
0 1 0 : 1;
0 1 1 : 0;
1 0 0 : 1;
1 0 1 : 0;
1 1 0 : 0;
1 1 1 : 1;
endtable
endprimitive
primitive summ(sum, cin, a, b);//进位
output cout;
input a,b,cin;
table
//cin a b : sum
0 0 0 : 0;
0 0 1 : 0;
0 1 0 : 0;
0 1 1 : 1;
1 0 0 : 0;
1 0 1 : 1;
1 1 0 : 1;
1 1 1 : 1;
endtable
endprimitive
7.2UDP应用实例
(1)组合电路UDP元件
组合逻辑电路的功能列表类似真值表,就是规定了不同的输入值和对应的输出值,表中每一行形式是output,input1,input2,…,排列顺序和端口列表中的顺序相同,合电路UDP的输入端口可多至10个。如果某个输入组合没有定义的输出,那么就把这种情况的输出置为x。
例:3选1多路选择器
primitive mux3_1(Y,in0,in1,in2,s2,s1);
input in0,in1,in2,s2,s1;
output Y;
table
//in0 in1 in2 s2 s1 : Y
0 ? ? 0 0 : 0;//当s2s1=00时,Y=in0
1 ? ? 0 0 : 1;
? 0 ? 0 1 : 0;//当s2s1=01时,Y=in1
? 1 ? 0 1 : 1;
? ? 0 1 ? : 0;//当s2s1=1?时,Y=in2
? ? 1 1 ? : 1;
0 0 ? 0 ? : 0;
1 1 ? 0 ? : 1;
0 ? 0 ? 0 : 0;
1 ? 1 ? 0 : 1;
? 0 0 ? 1 : 0;
? 1 1 ? 1 : 1;
endtable
endprimitive
时序电路UDP元件
UDP还可以描述具有电平触发和边沿触发特性的时序电路。时序电路拥有内部状态序列,其内部状态必须用寄存器变量进行建模,该寄存器的值就是时序电路的当前状态,它的下一个状态是山放在基元功能列表中的状态转换表决定的,而且寄存器的下一个状态就是这个时序电路UDP的输出值。所以,时序电路UDP由两部分组成一状态寄存器和状态列表。定义时序UDP的工 作也分为两部分一初始化状态寄存器和描述状态列表。
在时序电路的UDP描述中,01、Ox、xl代表着信号的上升沿。下面给出一个上升沿D触发器的UDP开发实例。
例:通过Verilog给出D触发器UDP描述,并在模块中调用UDP组件
primitive D_Edge(Q,Clk,Data);
output Q;
reg Q;
input Data,Clk;
initial Q = 0;
table
//Clk Data : Q(Stata): Q(next)
(01) 0 : ? : 0;
(01) 1 : ? : 1;
(0x) 1 : 1 : 1;
(0x) 0 : 0 : 0;
(?0) ? : ? : -;// 忽略时钟负边沿
? (??) : ? : -;// 忽略在稳定时钟上的数据变化
endtable
endprimitive;
表项(01)表示从0转换到1,表项(0x)表示从0转换到x,表项(?0)表示从任意值(0、1或x)转换到0,表项(??)表示任意转换,输出默认为x。假定D_Edge为UDP定义,它现在就能像基本门一样在模块中使用。
(3)混合电路UDP元件
在同一个表中能够混合电平触发和边沿触发项。在这种情况下,边沿变化在电平触发之前处理,即电平触发项覆盖边沿触发项。下面给出一段带异步清空的D触发器的UDP描述。
例:利用Verilog语言完成异步清零D触发器的UDP描述
primitive D_Async_FF(0, Clk, Clr, Data);
output Q;
reg Q;
input Clk, Data, Clr;
table
//Clk Clr Data : (SQtate) : Q(next)
(01) 0 0 : ? : 0;
(01) 0 0 : ? : 0;
(0x) 0 1 : 1 : 1;
(0x) 0 0 : 0 : 0;
(?0) 0 ? : ? : -;
(??) 0 ? : ? : -;
? 1 ? : ? : 0;
endtable
endprimitive