接口的强大功能:一是简化模块之间的连接;二是实现类和模块之间的通信。可以说接口的功能固然强大,但是问题又来了:
首先,因为事务交易处理器中的方法采用了层次化应用的方式去访问对应端口的信号,所以我们只能为两个相同功能的接口分别编写两个几乎一样的事务交易处理器,为什么呢?因为采用的是层次化的应用,假如设计中的某个引脚名字需要修改,我们只能修改驱动这个端口的方法!这样还是有点繁琐,那么sv中有了虚接口的概念,事情就会变得更加简单了!
到底是如何操作的呢?
虚接口和对应的通用方法可以把设计和验证平台分隔开来,保证其不受设计改动的影响。当我们对一个设计的引脚名字进行改动的时候,我们无须改动驱动这个接口的方法,而是只需要在例化该事务交易处理器的时候,给虚接口绑定对应连接的实体接口即可。以此来实现事务交易处理器的更大重用性。
虚接口的定义:
virtual interface_type variable;
虚接口可以定义为类的一个成员,可以通过构造函数的参数或者过程进行初始化。
虚接口应用的具体步骤:
1.定义接口(Sbus),该接口可供所有具有相同接口的模块或者类使用
2.在事务交易处理器的类中(Sbus_transactor)添加一个对应接口类型(Sbus)的虚接口成员(bus)
3.在事务交易处理器的构造函数中,添加一个对应接口类型的虚接口的参数(s)
4.在事务交易处理器的构造函数体内将参数(s)赋值给虚接口成员(Sbus)
到此,我们就可以在事务交易处理器中,编写针对该接口的通用方法(如request和wait_for_bus),只要针对虚接口进行操作就可以,而该虚接口不针对特定的具体器件,只有在事务交易处理器的对象例化创建时,根据具体传递给他参数确定。
5.在被测设计同一个层次例化实体接口(s[1:4]())。
6.将被测设计(a1/b1/a2/b2)的例化端口和实体接口相连接
7.例化并创建事务处理器对象,并将实体接口作为参数传递给其例化。
例子:
interface SBus;//定义总线SBus接口---步骤1
logic req,grant;
logic[7:0] addr,data;
endinterface
class SBusTransactor; //SBus事务处理器
virtual SBus bus;//SBus类型的虚接口---步骤2
function new(virtual SBus s);//步骤3
bus = s;//初始化虚接口---步骤4
endfunction
task request();//请求获取总线控制权
bus.req<=1'b1;
endtask
task wait_for_bus();//等待授权
@(posedge bus.grant);
endtask
module devA(Sbus s).........endmodule//使用SBus为接口的设计
module devB(input req,output grant,input[7:0]addr,data).....endmodule
module top;
SBus s[1:4]();//例化4个接口----步骤5
devA a1(s[1]);//例化4个设计----步骤6a
devB b1(.req(s[2].req),.grant(s[2]).grant),.data(s[2].data),.addr(s[2].addr);//步骤6b
devA a2(s[3]);
devB b2(req(s[4].req),.grant(s[4]).grant),.data(s[4].data),.addr(s[4].addr);
initial
begin
SbusTransactor t[1:4];
t[1] =new(s[1]);//步骤7
t[2] =new(s[2]);
t[3] =new(s[3]);
t[4] =new(s[4]);
end
endmodule