systemverilog(四)连接设计和测试平台

1/测试平台与DUT之间的通信
在这里插入图片描述
在传统verilog中,测试平台的代码独立于设计代码,处于不同的模块中,但是使用模块来保存测试平台经常会引起驱动和采样的时序问题。所以systemverilog引入了程序块program block,从逻辑上和时间上分开测试平台。
模块之间的连接采用接口,是systemverilog中一种代表一捆连线的结构。(接口可以像模块那样例化,也可以向信号一样连接到端口)
在这里插入图片描述
使用端口的仲裁器模型(由于systemverilog 中reg类型可以像wire一样连接块,为区别verilog中的reg类型,logic(单驱动))

module arb_port(output logic[1:0] grant,
				input logic reset,
				input logic [1:0] request,
				input logic clk);
...
endmodule	

使用端口的测试平台(测试平台通过端口与设计连接)

module test(input logic [1:0] grant,
			output logic [1:0] request,
			input logic clk,
			output reset);
initial
begin
	@(posedge clk) request<=2'b01;
	$display("@%0t:req=01",$time);
	...
	$finish
end
endmodule

顶层连接测试平台和DUT,并且含有一个简单的时钟发生器(clk generator)
最后,采用例化的方式

module top;
logic [1:0] grant,request;
bit clk,rst;
always #5 clk=~clk;
arb_port a1(grant,request,rst,clk);
test b1(grant,request,rst.clk);
endmodule

但是真实的设计往往有数百个端口信号,声明信号和端口复杂困难
在这里插入图片描述
可以采用接口简化连接

interface arb_if(input bit clk);
logic [1:0] grant,request;
logic rst;
endinterface

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果希望在一个接口中放入一个新的信号,只需要在接口定义和实际使用这个接口的模块中做修改,而不需要改变其他任何模块。

连接接口和端口(可以将接口的信号直接连接到每个端口上)
在这里插入图片描述
使用modport将接口中的信号分组
在接口中使用modport结构能够将信号分组并指定方向

interface arb_if(input bit clk);
logic [1:0]grant,request;
logic rst;
modport TEST (output request,rst,
				input grant,clk);
modport DUT(input request,rst,clk,
			output grant);
modport MONITOR(input request,grant,rst,clk);
endinterface				

需要将modport名即DUT或者TEST放在接口名即arb_if的后面,其余与之前相同。
在这里插入图片描述
在这里插入图片描述
两种方法使用modport名,可以使用接口信号的程序和模块中使用modport名,也可以在顶层模块中使用modport名,然后把接口放在程序和模块的端口表中。

总线设计中需要为主、从设备和仲裁器定义三个modport,此外还需要一个监视modport,下面是一个仲裁器监视模块
在这里插入图片描述
(在接口中不能例化模块,但是可以例化其他接口)

激励时序
1、使用时钟块控制同步信号的时序(时钟块中的任何信号都被同步地驱动或采样,保证测试平台的信号交互)
一个接口可以包含多个时钟块,因为每个块都只有一个时钟表达式,即对应一个时钟域,@(posedge clk)定义单时钟沿,@(clk)定义双时钟沿;可以使用default语句指定一个时钟偏移;一旦定义时钟块,测试平台就可以用@arbif.cb表达式等待时钟,而不需要描述确切的时钟信号和边沿。

interface arb_if(input bit clk);
logic [1:0]grant,request;
logic rst;

clocking cb @(posedge clk);//s
output request;
input grant;
endclocking

modport TEST(clocking cb,
			output rst);
modport DUT(input request,rst,output grant);
endinterface

module test(arb_if.TEST arbif);
initial begin
arbif.cb.request<=0;
@arbif.cb;
$display("@%0t:%b",$time,arbif.cb.grant);
end
endmodule

如果测试平台在接口中使用过程复制语句驱动一个异步信号,那么该信号是logic类型的,而wire类型变量只能被连续赋值语句驱动。

2、避免使用#来解决时序问题

3、测试平台-设计间的竞争状态
(应当在时钟沿到达之前的Tsetup时间上采样)

4、程序块(program block)和时序区域(timing region)
如何将测试平台的事件与设计的事件分开调度?
(system verilog 引入一种新的时间片的划分方式)
在这里插入图片描述
在Active区域中,包括RTL、门级代码和时钟发生器(时间并不是单向前向流动-observed 和 Reactive 区域的事件可以触发本时钟周期内Active 区域中进一步的设计事件)
在这里插入图片描述
在verilog中,仿真在调度事件存在的时候会继续执行,直到遇到$finish,而system verilog把任何一个程序块都视为含有一个测试,如果仅有一个程序块,那么当完成所有initial块中的最后一个语句时,仿真就结束了。

5、指定设计和测试平台之间的延时
在这里插入图片描述
时钟块的默认时序在#1step 延时之后采样输入信号(1step 延时规定了信号在前一个时间片的postponed区域,在设计有任何新的动作之前被采样),在#0 延时之后驱动输出信号。

6、接口的驱动和采样
信号的同步:可以使用verilog的@和wait来同步测试平台的信号
例如:
@bus.cb; //在时钟块的有效时钟沿继续
@posedge bus.cb.grant; //上升沿继续
@negedge bus.cb.grant; //下降沿继续
wait (bus.cb.grant == 1); //等待表达式被执行,如果是真,不做任何延时

信号的采样:从时钟块中读取一个信号的时候,是在时钟沿之前得到采样值,例如postponed区域(注意此处使用的是=)
在这里插入图片描述
在这里插入图片描述
在接口中使用modport可以将信号分组并规定方向,任何同步接口信号都必须加上接口名arbif和时钟块名cb的前缀,例如arbif.cb.request是合法的,但arbif.request是非法的。

通过时钟块驱动接口信号:在时钟块中应当使用同步驱动,<=(由于信号在赋值后并不会立即改变,由于时钟块的默认输出延时是#0,所以使用<=,其值会立即传递到设计中)
在这里插入图片描述
在这里插入图片描述
接口的双向信号:在verilog中,需要用一个连续赋值语句将reg连接到wire

interface master_if(input bit clk);
wire [7:0 data;
clocking cb@(posedge clk);
	inout data;
endclocking
modport TEST(clocking cb);
endinterface

program test(master_if mif);
initial
begin
mif.cb.data<='z;//三态总线
@mif.cb;
$displayh(mif.cb.data);//从总线读取
@mif.cb;
mif.cb.data<=7'h5a;//驱动总线
@mif.cb;
mif.cb.data<='z;//释放总线
end
endprogram

在程序(program)中不允许使用always块,如果加入always块,永远不会结束,这样就不得不明确调用exit来退出。(如果非用不可,可以使用initial forever来代替)

时钟发生器不能放在程序块中,会引起信号的竞争,所有的时钟边沿使用阻塞赋值产生,将在active域触发事件的发生,如果需要在0时刻产生一个时钟边沿,那么可以使用非阻塞赋值语句设置初始值,这样所有的时钟敏感逻辑电路都会在时钟之前执行

module clock_generator (output bit clk);
initial
always #5 clk=~clk;
endmodule

将模块连接起来可以使用一个快捷符号.*(隐式端口连接),能自动在当前级别自动连接模块实例的端口到具体信号,只要端口和信号的名字和数据类型相同

注意·:端口列表中的接口必须连接

顶层作用域:在仿真过程中创建程序或者模块之外的对象,以便参与仿真的所有对象都可以访问它们,在verilog中,只有宏定义;system verilog引入编译单元,任何module,macromodule ,interface,program,package 或者primitive边界之外的作用域称为$unit ,在这个作用域内的任何成员,都类似于全局成员。

但是对于VCS,同时编译所有的system verilog代码,所以$unit是全局的;而DC一次编译一个模块或者一组模块,这时 $unit可能只包含一个或者几个文件的内容。

实例名$root允许从顶层作用域明确引用系统中的成员名,可以使用相对或绝对的方式来引用模块中的clk信号,如果模块是隐式例化的,即去掉top t1();那么在这个程序中的绝对引用要改成 $root.top.clk,如果打算做跨模块引用,使用顶层模块的显示例化。可以使用一个宏来保存路径层次,这样当路径改变的时候,只需要修改宏代码即可。

`timescale 1ns/1ns
parameter TIMEOUT=1_000_000;
top t1();

module top;
	bit clk;
	test t1(.*);
endmodule

`define TOP $root.top
program automatic test;
...
	initial begin
	$display("clk=%b",$root.top.clk);//绝对引用
	$display("clk=%b",`top.clk); // 使用宏
	$display("clk=%b",top.clk);//相对引用
endprogram	

system verilog断言
1、立即断言
使用if语句检查一个信号

bus.cb.request<=1;
repeat(2)@bus.cb;
if (bus.cb.grant !=2'b01)
	$display("Error,grant!=1");

简单的立即断言

bus.cb.request<=1;
repeat(2) @bus.cb;
a1:assert(bus.cb.grant==2'b01);

在这里插入图片描述
2、定制断言行为
一个立即断言有可选的then和else分句

a1:assert(bus.cb.grant==2'b01)
else $error("Grant not asserted")

在这里插入图片描述system verilog有四个输出消息的函数:$info, $warning, $error 和 $fatal;这些函数仅允许在断言内部使用,而不允许在过程代码中使用。

a1:assert (bus.cb.grant==2'b01)
	grants_received++;
else
	$error("Grant not asserted);

3、并发断言
可以认为是一个连续运行的模块,为整个仿真过程检查信号的值
下例,request信号除了在复位期间,其他任何时候都不能是X或Z

interface arb_if(input bit clk);
logic [1:0] grant,request;
logic rst;

property request_2state;
	@(posedge clk)disable iff(rst);
	$isunknown(request)==0;//确保没有z或者x存在
	endproperty
	assert_request_2state:assert property (request_2state);
endinterface
发布了64 篇原创文章 · 获赞 5 · 访问量 3202

猜你喜欢

转载自blog.csdn.net/buzhiquxiang/article/details/104060342