大家好,我是一哥,上章我们进行了事物级建模,以及构建事物发生器sequence,搭建一个可以运行的uvm测试平台,从本章我们开始创建agent当中的三个组件,sequencer,driver,monitor。
sequencer与sequence的名字非常接近,可以看出它们两者有着直接的联系。从uvm平台结构上来看,sequence item与sequence并不是平台结构的组成部分,他们是流动在平台中各个组件之间的数据流。
那么sequencer有什么样的作用呢?
一个作用是启动sequence,另一个sequence不能自己启动其中的body方法来产生事物对象,它必须有一个与之相关联的sequencer来启动。
前面介绍过,主要把驱动dut的数据来源是sequence,那么sequencer的第二个作用就是将sequence所产生的事物对象依次发生到driver当中去,sequencer可以说是用户使用起来最简单的组件了,因为它的绝大部分和功能已经有uvm源代码实现,所以说这边创建起来比较简单。
图中显示出来sequencer在平台中的作用,位置。一个简单的sequencer可以这么来定义,使用typedef为自己的sequencer重新定一个名字,叫my sequencer。uvm sequencer它是一个参数化的类。其中的参数表示它所能够向driver发送的事物的类型,因为要用sequencer来启动之前所创建的sequence。sequence产生的事物类型为my transaction,所以说这里的参数就是my transaction,这样呢,就完成了sequencer的创建。在继续学习创建driver之前,有必要首先来了解一下组件的一个重要的属性,phase机制。
uvm平台中的所有组件都有phase的概念,它们要按照一定的顺序执行的任务,或者是函数组成,组件中存在着以下主要的phase。
这些phase是组件中的任务,或者是函数的名称,验证工程师需要根据具体的情况对它们进行重载来实现自己想要实现的功能。当平台启动之后,会按照图中给出的顺序依次执行。比如,在仿真开始阶段先执行build phase。当所有组件了build phase完成之后,才能进入下一个connect phase,当report phase执行完毕之后整个仿真也就结束了。
这里,只需要了解关于phase的三点。第一点,这些phase存在于每一个组件当中,并且它们仅仅是任务或者是函数。
第二需要根据实际的情况对它们进行重载来实现我们想要的功能。
第三这些phase是按照uvm已经固定好的顺序自动执行的,不需要手动调用。有了对激励进行建模的事物类,也有了产生事物的事物发生器sequence。那么接下来就要构造driver将事物驱动到dut上了。
图中显示了driver在测试平台中的位置,driver主要有三个职能,在uvm中,driver自己不能够产生事物,所以说它的第一个职能就是从sequence那里获取事物对象,由于dut的输入,只能够接受pin级信号,所以说driver需要将抽象的事物对象进行分解,自己转化为dut可以接受的pin级信号。第三个职能就是将转化之后的pin级信号按照dut接口协议的要求将信号驱动给dut。
接下来,看看代码是如何实现driver的三个功能的,那么构建的driver需要从uvm driver进行扩展,uvm driver,它也是一个带参数的类,你需要为driver指定它所处理事物的类型。
那么,这里我们指定之前已经建立好的my transaction是事物类,与之前类似,这里调用uvm的一个宏来注册driver,对这个宏与之前的宏稍微有一点不同,记住一点,所有的平台组件的注册都是使用uvm component utils宏,而其他的都使用uvm object utils宏。
下面完成构造函数,注意,这里的构造函数比原来的多了一个参数,其类型是uvm component,名字为parent,从字面上来理解是这个参数指带的是driver在实例化之后的负对象的指针。
下面就要实现抓driver的功能了,这个功能选择在他的run phase中完成,这是一个任务,那么其参数是uvm phase类型,这里只需要先照办即可。需要在run phase中实现driver的三个功能。
那么,一般来说,driver,它是不停工作的,它要不断的获取事物,分解事物,驱动事物。
所以说在外围为它添加forever无限循环,首先需要从sequencer获取事物对象,调用seq_item_port.get_next_item(req)的方法,就可以从sequencer获取事物对象,而且使用iq这个指针来引用获取到的对象,iq的类型是开始定义driver类的时候所传入的参数的类型,那么这里就是my transaction。一旦这个方法执行成功,iq就指向了一个事物对象,要注意的是,每调用一次只会获取一个事物对象,那其实这一步就相当于driver,从sequencer相连的通道中获取事物对象,因为平台中的dut已经被简化掉了,所以说,这里不对获取的事物进行任何处理,只是简单的将其打印出来,在这里,调用uvm提供的uvm infer宏执行打印操作,关于uvm的信息打印机制,在后面会做详细的讲解。现在只需要知道这条语句是打印信息即可,在打印之后,等待100个时间单,然后就用seq_item_port.item_done语句,这条语句的作用是通知sequencer该事物已经处理完毕,那么正好为什么要通知sequencer已经把事物处理完了?回到之前的get_next_item语句,这条语句可以看作是driver向sequencer发出请求,那么driver也就需要对sequencer进行回应了,item_done可以看作是driver对于sequencer的响应,所以说driver后续事物,其实是由get_next_item以item_done一起协同完成,所以它们通常是乘成对使用。
接下来了创建监视器monitor,图中显示了monitor在平台中的位置,monitor的主要功能是监视接口上的信号,一旦遇到活动的事物,就将接口的信号收集下来,并将这些信号按照一定的格式打包成抽象级的事物对象,然后再将这些对象发送到平台组件的分析组件去,作为分析组件数据的来源,monitor要与其他组件进行通讯需要借助uvm的通讯机制,part。这个通讯机制会在以后的课程中做详细的讲解,下面来看一下monitor的代码实现。
同样创建的monitor需要从uvm monitor这个基类扩展,使用宏注册monitor,完成构造函数与driver类似,monitor的功能,我们这里在run phase中完成,由于简化的平台当中没有dut,也没有接口,所以,这里的monitor只是简单的打印了一些信息,这个monitor的功能非常简单,就是每经过100个时间单位就打印monitor run。
本节课主要学习了一个简单的sequencer,driver以及monitor的创建过程。在下一节课中,会学习如何将这三者封装到一个agent当中。