笔记 sequence

记录

为什么是UVM?

  1. verilog/VHDL/SystemC 设计的TestBench
  2. CL(Systemverilog Base Class Library)
  3. 随机约束验证(random constrained verifacation)(核心):是用仿真中的随机向量。
    有两个好处:1.约束随机 仿真非常适合发现bugs。
    2.自动生成激励(长时间仿真等)
    验证中的CCC指的是:
    1.checker 2.coverage (验证运行发生的情况,识别对设计的实践有多彻底) 3.constraint (有约束=》auto checking的TB平台。有覆盖率空洞=》调整约束)

UVM中复用的基本单元是代理,有时也称为通用验证组件。DUT上每个主要接口都有一个agent。接口是指 总线接口,分组交换接口,串行接口或者其他接口。每个agent都有一个校准结构。包括:下游路径上的sequencer和driver,他们生成激励,并将这些激励注入到DUT中。上游路径上的monitor,正在观测接口上的引脚,将这些引脚组装成 transaction,然后将这些transaction分发到验证环境的其余部分进行检查和功能覆盖率分析。

验证环境之间的通信都是事务级别的。在sequencer运行的这些事务最开始都是在sequence生成。所以sequencer和sequence之间的关系很重要。sequence是动态的,他们在 sequencer上运行,生成transaction。sequencer是准静态的。他们在UVM
build phase 的0时刻create。 monitor 也生成transraction,将这些transaction 发送到验证环境的其他部分。

聚焦于 sequencer和driver之间的通信。(sequencer和driver都是在验证环境的组件层次结构中实例化的UVM组件,driver是连接DUT和sv 接口。driver通过从配置数据库中检索sv 接口的位置,来找到他的位置,该配置数据库是 有TOP层级 set的)。

sequencer和 driver之间的通信是 事务级的,也就是TLM ports和 TLM exports。 类似于VHDL或者verilog中的端口,允许你在组件之间通信,并且他们允许通信模块化。这样各个组件就不需要知道他们正在通信的组件的通信细节。所以,在这种情况下,driver通过port 进行通信,sequencer通过export进行通信,但driver和seuqnecer实际上都不知道他们正在通信的组件的身份。常见的TLM是 事物级建模。而TLM port和export实际上是从system C借鉴过来的。

正如所见,我们在sequencer上运行一个sequence,该sequence将生成 transaction,并且通过 事务级接口将这些事务传递给driver。

正如所看见的,driver和sequencer之间有一个 pull接口。也就是说,driver是通过 TLM端口(基本上是事务级的通信) 从sequencer pulling或者得到 transaction。这就意味着 是使用函数调用进行的通信,其中每个函数 调用的参数都是对transaction的引用,我们将在适当的时候看到所有的源代码。

让我们从一个transaction开始:

定义一个数据类型,通过拓展类UVM sequence item的类,此类是基于UVM的类库中的类之一,它表示可以从 第二行的sequence生成的item。使用UVM obejct utils 为工厂自动化注册类;之前都是用 uvm conponent utils将组建注册到工厂。但是 transaction并不是组件,它在组件层级结构没有父级。它只是一个对象,它使用UVM objection 详细信息进行注册,然后我们得到了 treanscion的各个字段,本例是命令,地址和数据,因此在这里 可以定义描述特定协议所需要的任何字段,我们在每个属性 前面添加关键字 rand,这样我们随机化的时候,他们就会被随机化,就会有随机化的事务对象。然后我们就有一些系统总体 的约束,这只是为了确保transaction 和事务对象在随机化的时候,事务的各个字段至少具有合理的合法值,然后我们是事务类的构造函数,transaciton的构造函数 采用所示的形式,因此transraction 不是组件, transaction在组件层次结构 没有父级,因此构造函数 没有父级参数,而是只有一个默认值的字符串的name 参数。始终在transraction的字符串名称上包含默认值是一个好注意,因为在UVM基类库中的某些地方,UVM会在 不命名的情况下生成事务。
我们在定义事务类的情况下,事务类包含的某些方法会非常方便,因此我们在这看到的第一个这样的方法是 转换字符串。因此 转换字符串 只是一个函数,他返回 格式化为单个文本字符串的事务内容,因此我建议 每当 定义一个自己的事务类的时候,都为其提供一个确认字符串的方法,这个在转换为字符串的过程中非常方便,只需使用 $sformaft 即可实际获取。

sequence

sequence的作用主要是生成测试激励,sequence 是在sequencer上运行。因此 sequence 会在sequencer执行,在sequencer上运行的sequence会生成transaction。driver都很熟,driver 从运行在sequencer的 sequence 获取 get transaction,然后驱动 pins 。 monitor 查看 那些引脚的摆动,将信息组装成进一步的transraction,然后分发到其余验证环境用于分析。整个验证环境被划分为agent,每个agent 对应DUT的接口。然后可以通过所谓的viertual sequence 协调或者 编排多个agent的行为,因此virtual sequence 会启动在几个不同sequencer上运行的多个sequencer。每个sequencer都与DUT上不同的接口相关联。

sequence是一个拓展了基类UVM sequence的类的对象,uvm sequence 实际上是用 sequence item或者 transaction的类型的参数化,该sequence将会生成。sequence item 是一个事务,但是如果要声明 事务,从 uvm sequence item extend。在这种情况下,我们有一个类时钟和数据默认序列,我们自定义的sequence类 拓展了UVM sequence,并且使用事务类型transaction 参数。

TX sequence类,然后宏注册对象到工厂自动化。uvm object utils
包含一个构造函数,像其他UVM obejcts一样,构造函数只有一个参数,也就是对象字符串的名称。

sequence 特点是有body task。

body task执行sequence 所做的的所有操作。sequence 可以执行很多不同的操作。
每一个sequence都会有一个body task,但task主体任务通常会不同。

body可以做:
生成transaction
可以 start 启动 其他sequence。
或者sequence 正在为DUT中的寄存器生成激励,此时sequence可以简单的通过UVM register读取和写入寄存器。

如果在生成transaction 的作用下,拓展基类命名的 sequence item 是非常重要的。
如果使用sequence启动其他sequence或者使用sequence读取或者写入寄存器 stransaction并不直接相关,因此它 甚至可以被发出。

sequence生成 transaction 典型的主体任务:

此特定sequence 只仅仅生成了单个transaction,有时你的确会看到生成单个transaction。更常见的是,一个sequence会生成多个transaction。但是在此情况下,我们只从生成一个transaction的最小sequence 开始,(四个步骤)

  1. 创建 交易对象。用create方法。data-type :: type_id::create("req");//工厂方法,使用工厂方法 来创建一个新的transaction对象
    注意:变量的名称 要和字符串参数的名称相同。这只是良好的编程习惯。
    2.调用 start item。start item通知driver 此特定sequence 已经准备好发送请求。
  2. 然后从start_item 返回时,sequence 会随机化transaction对象,可能会带有一些内联的约束,我们稍后会看到一个内联约束的示例,如果随机化发生由于某种约束冲突而失败,那么我们将会收到一个错误。
  3. sequence调用finish item,而finish item会完成sequence 和正在 pull的driver之间的特定的握手协议。也就是中介 从sequence pull 下transaction的drive之间的特定握手协议的生命周期。(通过调用finish item 将事务发布给driver)

上述四步的替代方法。 uvm_do(req)、、
从编写过程式编码风格开始,明确调用 create;start_item;randomize 和finish_item.随着变得更加专家。可以用uvm do宏,但是要非常小心,警惕作为初学者使用uvm do,可能会发现 如果犯了一些错误,他是一个宏的事实可能会隐藏真正正在发生的事情。
接着通过查看从sequence中获取事物的driver 来完成此笔交易。

这里是driver的run phase方法。
因此,在driver会有一个无限循环。
driver 调用 get next item 来从sequence请求下一个事务。
sequence提供transaction时,driver 正在调用do_drive方法。该方法将摆动的接口中的引脚 到DUT.。
最后,driver调用seq_item_port的item_done方法告知sequence已经完成。因此,在生成transaction的 sequencer上运行的sequence 与 pull 事务的driver 的run phase方法之间 实际上在后台进行握手。

握手原理:

假设 sequence 通过 调用start_item 来启动。也就是如果sequence 调用start_item,则start_item 或者暂停,知道driver准备就绪,来获取下一个transaction。因此如果我们假设,首先调用start_item ,那么start_item 实际上会阻塞,直到driver 调用get_next_item。
当get_next_item时,driver 会告诉sequence 他已经准备好 摆动引脚,因此,此时我们 start_item 返回,sequence有机会将 在发送给driver之前 对transaction对事务进行随机化,因此 sequence 会快速连续地从start_item 调用randomize 返回,然后调用fnish_item,而对finish item的调用会将 transaction 发送给 driver。因此当调用finish_item时,driver端 对get next_item的调用会返回,然后轮到driver 处理该transaction。执行它需要执行的操作。例如:摆动接口的引脚。在此期间,finish item会被阻塞。知道driver 通过调用 item-done 想sequence宣布它已完成。所以,driver通过finish 通过调用 item_done来完成操作。此时,sequence从阻塞方法 finish_item 返回。如果driver 首先调用get_next_item,我们将会得到一个非常不同的事件的过程。

如果driver 首先调用get_next_item,我们将会得到一个非常不同的事件的过程。

假设 driver 首先调用 get_next_item, 但此时,sequence尚未准备好,那么“get next item”将会 将会一直阻塞,直到sequence 准备好为止。get next item实际上会一直阻塞,直到调用 “finish_item”。sequence准备好,它将会调用 start_item。start_item看到driver已经准备就绪,因此它将立即返回。然后,sequence可以随机化transaction 并调用 randomize,finish_item,finish_item将事务发送给driver,然后 事情按照上一张幻灯片。

启动sequence的方法:

首先从 另一个sequence启动sequence开始。
首先看一下父sequence的body。该父sequence 将启动子sequence。

第一步:一个sequence想要启动另一个sequence,至少需要执行第一步 创建 子sequence的对象。再次使用工厂方法调用,因此它是一下类型,子序列类类型::type_id::create 方法。确保参数字符串名字和变量名称匹配。

然后 父sequence可以继续通过调用其启动方法来启动该子sequence来运行。
start()

第一个参数时启动子sequence的sequencer的引用;第二个参数 是对父sequence的引用。总是 this。
所以我们在这个sequencer m_clkndata。m_sequencer 启动我们的子序列。并确保将父sequence的第二个参数设置为我们当前正在执行其body方法的当前sequence。
一个sequence想要启动另一个sequence。但是我们要做的事情更多,不仅仅是创建一个子sequence。创建子序列对象后,对其随机化是一个非常好的做法。

因此更好的做法是:rand ,子sequence类恰好包含任何 rand变量,则对子sequence对象进行随机化,然后在sequencer上启动该随机序列的对象。

如果我们关心随机的稳定性,可能还想插入另一个步骤。因此更完整的解决方案是:创建我们的子sequence,然后 调用设置项目上下文,以便于该序列对象设置一些上下文本信息。set_item_context来实现随机稳定性。通常,随机稳定性内置于UVM中,因为UVM的大多数对象都是根据对象的层次路径名和UVM 组件层次结构中的组件进行传播的。但是有一个例外,那就是sequence对象。因此,如果手动创建并随机化sequence对象,并且关心随机稳定性,最好插入ERT这个额外的步骤。如第二步所示,我们随机化sequence对象之前,我们会根据要运行的sequencer的完整层次结构来回溯sequence对象。为了完整启动,在启动sequence之前,我们要设置它phase的启动。设置staring phase启动原因是为为了告诉 sequence正在UVM那个 phase阶段运行,以便于sequence可以对该phase raise 和drop。

从组件启动sequence

就3个不同:
1.是在run_phase 执行的 而不是父sequence body
2.参数parent sequence都是null
3.我们已经知道此sequence的上下文。稍微不同方式确定phase,而不是通过调用获取。该phase 阶段 可以用作run phase方法的参数。因此我们在设置set starting phase 的调用使用 run phase的参数。

virtual sequence

通常是在UVM中用于指 恰好在外面的sequence和我们环境env中的agent。他们的工作是协调正在产生的激励的行为。跨多个接口到DUT。通常virtual sequence 会启动在agent的sequencer上运行的sequence。但是virtual sequence本身并不在 agent sequencer上运行。它在某个env上运行。

什么是virtual sequence?

  1. virtual sequence 是一个sequence。也就是说它最终是extend 于UVM sequence。
  2. 是一个 在其他sequencer上启动sequence的sequence。
    3.是一个 不产生transaction的sequence。//不一定
    4.是一个读写寄存器的sequence等 //

如果有一个sequence不生成transaction,则该sequence不需要在sequencer上运行。

sequence只需要在sequencer上运行,当sequence产生transaction。如果没有生成transaction,则是否在sequencer运行是可选择的。

这就是专家建议:virtual sequence工作是启动其他sequence。本身不需要在virtual sequencer上运行。

virtual sequence工作就是为了 orchestrate 其他sequence的行为。

和之前的sequence启动非常相似。
1.create vietual sequence
2. 随机化sequence object
额外步骤:设置一些变量 使得virtual sequence能够定位sequencer,它将能够start 子sequence 在sequencer
因此,我们实际在virtual sequence 对象分配了几个变量(在我们start sequence之前),然而不是在sequencer上运行我们的virtual sequence。我们已经在一个 空sequencer上运行sequencer。null作为start的参数。我们可以选择在sequencer上运行virtual sequence。
从sequence获取配置信息
sequencer的一个挑战是我们如何访问sequence的配置信息。
remember,sequence通常会生成代表着激励的transaction,并且你可能希望使用配置数据库。或者使用配置数据库中的信息进行参数化。

使用sequence的技巧。pick up 配置信息基于它所在的sequencer。配置特对象具有特定agent。
所以在env的build phase,我们使用config db来set 配置对象与特定agent。同时还想配置对象和该agent中的sequencer相关联。显式地,这可以痛过实例 字符串中的通配符来完成。
但是相反,我们又两个单独的调用

上面是我们的sequence。
我们的sequence 可以做的事 检索配置对象,通过获取与正在运行sequence的sequencer相关联的配置对象。 正在运行sequencer可以通过 get_sequencer() 来找到。get_sequencer实际上事基于uvm sequence的类的一个方法。任何sequence可以通过get sequencer 找到当前正在运行的sequencer。然后该sequence从配置中获取与该sequencer 相关联的配置对象。

我们定义了一个count。表示这个特定序列将生成多少个transaction事务。所以我们有一个for循环。最多由配置对象中的count的变量来确定和限制。每次循环都会创建一个新transaction,start事务。然后使用内联约束随机化事务。使得事物的数据值实际上是循环计数。所以在这,我们将生成一系列的事务。其中数据从0开始递增 并最终达到count-1。

sequence的细节:

工作:
sequence事在sequencer上运行的。sequencer事可复用的验证组件,它们拓展了UVM组件类

在UVM build phase 构建组件层级结构时,你可以实例化sequencer和driver
。另一方面sequence是object对象,这些对象包含有一个body方法,该body方法在模拟运行时 动态执行。也就是在UVM的 run phase。单个sequence 通常会生成 丛driver pull的transaction。每一个driver内置一个TLM port端口。而uvm sequencer内置一个 TLM export端口。在UVM build phase,将port连接到 export。然后在run phase,在sequencer上运行sequence生成transaction(通过start_item 和finish_item)。driver通过 get从sequence中获取transaction。

它的工作方式时 start_item 和finish_item。这些都是由sequence的body 做的。sequence和driver发出的调用get握手。因此,实际上,只有在调用get时才返回start_item。所以start_item和get 标记为一个同步点。sequence和driver之间的控制流仅会在start_item和get都被调用才会继续。因此当调用get时,start_item 将返回。然后 sequence 调用finish_item 来完成实际的transaction并将其发送给driver。回到driver,只有当finish_item被调用,我们才会从get返回。

UVM允许TLM通信或者验证环境中所有基于componet的类的组件之间事务级建模连接。

在此图中,可以看到sequencer和driver之间的事务级连接,还有事务级连接 用于分发来自agent的tranaction,在env的其他组件进行分析(覆盖率收集 checking)。
上图时将会在UVM环境遇到的TLM连接的类型

UVM的 transaction level modeling(TLM)

事务级建模简化为基本的要素就是使用函数的调用进行通信。所以在构造块之间进行通信或者通过执行task来在我们的UVM环境中的组件或者在一个组件中申明的函数可用于从另一个组件调用。

实际上,没有什么可以阻止driver 直接从sequencer中调用get任务,前提是 driver知道它的名称。但是这会在driver之间产生依赖关系:在driver和sequencer。而且代码难以维护。在UVM中,我们通过所谓的port和export 间接的连接(借鉴于systemC),实际上UVM借鉴了sysmtemC的tlm1标准。在sv中重新实现。现在不是driver直接调用sequencer的get,而是driver通过port连接到sequencer的export。因此export的作用是申明特定组件将提供特定任务或函数。

所以在此例子中,uvm sequence item pull implementation。说我们的sequencer 提供了get任务的实现。


port申明特定组件需要 一个特定的任务可用 (require a get())。 在本例中,uvm sequence item pull port 表示我们的driver 将调用get函数。

agent中,然后在top 实例化 sequencer时,我们将port 连接到export的driver 。这有效的签订了provider和 特定方法的要求之间合同。 我们使用connect的 sequence item port的方法。UVM内置的,以便于将driver上的port连接到sequencer上的export。

困惑:export,你假设有东西从sequencer传出,从sequencer发出的并不是对get任务的调用(相反 export 反映了 sequencer 正在exporting的事实或者提供get任务)以便于driver可以调用它。想象,task正在提供,从左到右完成事务从左到右的传递,但是get任务的调用却是从右到左

在这个例子,有个agent,我们有一个连接在driver的port和 sequencer的export,所以这里
seq_item_port.conncet(seqr.seq_item_expot).这都是在connect phase之中。

driver的run phase:
调用 seq_item_port的方法 get_next_iem。

TLM特定的协议:

put(req) get(req) write(tx)

put用于发送事务,是一个阻塞方法,意味着put不会返回,直到transaction的接受方 准备好下一个对put的调用。结果就是,如果进行一系列的调用 每个事务transaction都从头到尾传递,所以put是保准不会覆盖或者丢失任何对象。这同样适用于get,这也是一个阻塞方法。因此,呼叫者 呼叫可以请求 caller calls get to ask for a request,以及get块的实现,直到它准备好返回transaction,直到请求准备就绪。当get返回时,对象被有效的删除从右侧删除的,这样下次调用get时,将会收到流的下一个事务。这些解释结果就是一系列的调用put或者get将传递transaction流不会丢失任何transaction,让这些调用非常非常好。
第三种方法是正确的方法,和分析port一起使用的非阻塞方法。意味着不消耗任何时间,但是立即返回。正因为如此,implementation的权利 必须立即处理作为参数传递的transaction。因此,当被要求写入时,知道它应该立即被处理 并立即返回了。

你可以使用这些put get和weite方法来实现 push连接或者 pull连接,我们在这个slide 看到每个连接的示例。因此在TOP,我们有一个对put的调用,它左侧的生产者 和右侧消费者之间实现了 push 连接。因此,生产者 调用了 put 将trancation push给消费者 ,消费者在幻灯片底部执行put 任务。有一个 pull 拉取连接,因此左侧的consumer 再次调用get ,从右侧生产者出拉取事务。来自右侧生产者的交易再次是生产者。左侧的生产者执行的get 任务,因此 您可能发现 这里与TLM连接存在的混淆的概念,您必须始终注意控制流方向和 和数据流方向之间的对比,因此在使用 push 连接时,控制时从左到右流动,事务也是从左到右传递。

但是 使用 pull连接的时候,控制是从左到右流动,消费者调用 get方法,但是事务时从右到左的传递,生产者 将 事务返回给消费者。因此,在TLM的世界中,port允许 组件调用自身外部申明的task或者函数,而export 允许组件接收对组件内部实现的任务或者函数的传入调用。圆圈表示export 标记为 或者imp。区别在于 export表示时中间节点waypoint, 但是imp却是路线的终点。表示该任务或者函数实际上要 实际的位置。因此,如果该组件有export,则该组件会将transaction传递给子组件来处理。如果该组件有imp,则该组件本身必须提供相应任务或者函数的实现。我们具体看个例子:

左侧有端口和对put方法的调用,这些方法沿着 层次结构向上传递;在图的右侧我们有 export和对put方法的调用,这些方法沿着组件向下传递;因此 port用于在组件外进行方法的调用;export 用于在组件内部进行方法的调用。因此,在左侧,我们有一个子组件,通过其端口调用put,子组件的port连接到父组件的端口。直到道道组件层次结构的顶端。连接到右侧组件的export;然后,export 连接到export,沿着层次结构向下传递,直到最终连接终止与imp ,而具有imp的组件有义务 实际提供相应方法的实现。在此特定情况下为put 任务。port只是用于 将方法调用从组件传递出去;export用于 调用传递到组件内部;在top层次结构中,可以在port和export之间建立 点对点的连接。

分析接口 analysis port

analysis port 提供了一种广播机制。analysis port 允许agent 发送事务进行分析,但是 agent既不知道 也不关心transaction的去向。事实上,analysis port主要特性时 根本不需要连接他们。例子中,monitor上有一个分析端口,此端口连接到 其父组件agent的上的分析端口,但此agent上的分析端口 不必连接到任何其他地方,或者它可以连接到另一个组件(subsciber)

订阅者时可以订阅来接收的通过analysis port 发送的 transaction 的组件,并且analysis port可以设置 连接到 0,1或者多个订阅者。这是分析口的主要特点。

它们不需要连接,也可以连接多个subscribers。这里的subscriber 有时UVM组件中的检查或者覆盖率收集组件。


我们通过analysis port 调用的方法只有write ,因此 monnitor正在调用其分析端口的 write方法,该write方法 通过 agent的analysis port向上传播,然后传递给连接到该analysis port的所有订阅者。write方法只有一个输入参数。write方法只能读取传入transaction的值。write方法不允许修改,他实际上接收到事务的值,也就是可读的。write方法时非阻塞的。它必须使用 transaction 并立即返回,因为write方法会立即返回 并且不允许修改它接收的事务。各个订阅者的write方法调用顺序不重要。实际上你从monitor analysis port调用write方法,无法控制各个订阅者的write方法调用顺序。

提供乐意一对多的形式的连接。一个分析口能够将多事务广播给多个订阅者,这个和常规port 和export形成对比。常规port和export严格来说是一个一对一的。

driver 和seuqncer之间有pull的连接;driver 调用get或者get_next_item 从sequencer pull出事务;另一方面,analysis port 提供push连接。write方法 push transaction 通过agent内的analysis prot将transaction push出去。

右侧driver port和sequencer export时点对点的连接;
另一方面,我们有一个子到父的连接的示例。因此,monitor的analysis port,子组件 连接到其父agent上的 analysis port。然后 agent上analysis port 和三个订阅者上的export 之间建立了点对点的连接。

在类中 申明 analysis port。通过调用构造函数 new函数来 构造analysis port。analysis port 必须使用new不能用工厂方法(不支持机制))。

查看设计和 测试过程中发生引脚的摆动,将引脚摆动组装为事务对象,然后通过调用analysis port 的write方法 将事务对象通过 分析板发送出去。这是UVM的典型编码模式,monitor 通常会查看引脚的摆动,将引脚的摆动组装成transaction,并通过analysis port 发送事务。现在组件层级结构向上移动了。

在此时agent的connetct phase阶段。
可以在top建立两个连接,monitor上的analysis port 连接到 agent上的 analysis port。再次看到driver 上的 port 和 sequencer上的 export。

然后组件层级结构再向上移动,我们到达 特定agent的最终类,因此在 env的 build phase中,


我们实例化 agent的嘴贱 和 订阅者或者覆盖率收集组件,然后在此类的 connect phase,我们调用analysis port 的连接方法,将代理的analysis port 连接到 订阅者上的analysis export。


现在我们可以查看订阅者,因此UVM订阅者 时UVM标准基类之一,它extend了 UVM component,换句话说,UVM subscriber时另一种组件,就像driver monitor agent一样。都是各种组件。UVM subscriber 特殊之处在于 内置一个分析的imp

在图上看到的export或者analysis imp实际上是在 uvm subscriber基类中实现的,extend了 compont类。使用此类时,只需要实现于该分析imp关联的正确方法,每个具有analysis port imp的组件必须提供相应的正确方法,因此在,代码底部,可以看到正确方法的实现。始终和覆盖率类的方法,顺便说一下,如果有一个订阅者,但是没有正确实现的方法,将会编译报错。因此这个特定的subscriber被用来收集一些覆盖率。这是uvm subscriber 通常会做的事。同时检查 subscriber 类是否具有 覆盖组,然后在正确的方法中,我们使用它获取传入的transaction,来分配一个局部变量,然后调用覆盖组的采样方法对覆盖率进行采样,并将这些值写入覆盖率数据库,我们还会检查该特定覆盖组的覆盖率是否达到00%,以达到,我们将 将设置 m已经覆盖。

加深理解tlm技术。

第一种 处理多个传入transaction。
第二种 存储分析事务以供以后使用(如果不想处理这些transaction)

处理多个传入transaction

处理多个传入的transaction,我们会 为你生成一个 reference model。rm通常必须处理 多个传入数据流,在UVM中执行此操作,我们面临的挑战时每个transction都需要 有一个与之关联的正确方法write来处理传入analysis export。但sv不支持函数的重载,意味着在sv 单个类中,不能有多个重名的方法,所以为了绕过sv语言的限制,UVM提供一个辅助宏 uvm_analysis_imp_decl(_0)
随意使用这个宏的作用是声明是一个新类和一个关联的write方法。该类名为uvm_analysis_imp_0申明为参数宏的参数,这些宏创建了两个新类我们用它们来声明 analysis export变量。还声明了两个新方法 write_0/1来替代 默认的 write方法。请注意,我们的参考模型不是一个订阅者,而是一个组件,因为他有两个传入的事务流。如果我们只有一个传入transaction流,我们只能使用uvm subscriber。因为uvm subscriber只有一个analysi export。

第二种 存储分析事务以供以后使用(如果不想处理这些transaction)

如果在订阅者,你不想立即传入 transaction。而是将其保存以供以后使用,我们可以使用常规的 uvm conponent组件来代替uvm subscriber,然后在该组件内,将事务转发到fifo,

换句话说,我们通过analysis export 接收传入的交易,然后在内部将该 transaction 发布到fifo,以便于在稍后可以在闲暇时使用它,uvm提供了一个fifo。uvm_tlm_analysis_fifo 专门用于此目的。
所以analysis export 和fifo均未注册工厂自动化,因此在 new的构造函数中 new中实例化这些对象,然后 传入 analysis export 和 fifo组件上分 analysis export之间创建了子组件到父组件。因此,通过analysis port传入的任何交易现在都将直接转发给fifo,fifo实际上具有uvm analysis imp 和内置的write方法,然后在fifo 订阅者执行 run phase,在无限循环中,我们调用一个阻塞的get方法从fifo中获取下一个transaction。因此,当我们第一次通过这个循环时,如果订阅者尚未从agent中收到任何analysis 交易,则get将阻塞,直到第一个analysis transaction

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值