热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

UVM:sequence层次化

目录1.flatsequence2.hierarchicalsequence多agent情形3.virtualsequence3.1.例子3.2.hierarchicalvirtu

目录

  • 1. flat sequence
  • 2. hierarchical sequence
    • 多agent情形
  • 3. virtual sequence
    • 3.1. 例子
    • 3.2. hierarchical virtual sequence
      • 例子
  • 4. layer sequence
    • 4.1. 问题阐述
    • 4.2. 每个layer 对应一个sequence
  • 5. 本质:`uvm_declare_p_sequencer(my_sequencer)
      • 只可在body()内使用p_sequencer
    • 5.1. 心跳



本篇将sequence的构建,一个dut有很多个driver,每个driver要配一个sequencer,而每个sequencer可能需要准备很多个事物产生器,即sequence,那该如何组织呢?

1. flat sequence

关于trans的随机化,我们可以不深入sequence::body()内部改变trans的硬约束以产生不同的测试用例,而是在sequence声明一些随机变量,通过随机化sequence实现对trans的随机化,即flat sequence。

下面结合了之前讲到的sequence机制代码给出一个例子。
在这里插入图片描述
在这里插入图片描述

如红色箭头所示,通过硬约束seq实现了seq::body()中产生的trans的随机化控制,这样每次改变测试用例只需在test::run_phase中改seq.randomize()的硬约束即可,不用深入seq内部啦!

尽量使transaction中包含最大的数据包,例如能包含一个数据包data[]就不要只包含一个数据data,否则会使sequence的属性更复杂


2. hierarchical sequence

既然能够通过对seq的随机化控制实现对seq中trans的随机化控制,那么也可以嵌套sequence也可以实现对多个sequence的控制

比如将上面代码中的test类重写,并添加一个chnl_top_sequence

在这里插入图片描述

如上图所示代码,在test::run_phase()中只有一个chnl_top_sequence了,而在chnl_top_sequence::body()中实现了多个其他flat sequence的控制。

注意chnl_top_sequence根本就没有发送trans。

但有一个问题,不创建chnl_top_sequence类,将chnl_top_sequence::body()中的内容原封不动地放入test::run_phase()
phase.raise_objection(phase);phase.drop_objection(phase);之间,是不是也能实现上述效果,而且似乎代码量也没啥变化。


答:的确如此。但是可别忘了,这里我们举的例子中只有一个agent,实际应用中一个env要管理好几个agent,那样的话将所有的sequence都放在test::run_phase()中运行就显得代码冗长、可读性差。
那么如果一个env有很多个agent,每个agent有一个sequencer,每个sequencer需要好几个sequence同步驱动,那又该如何组织sequence呢?


多agent情形

思路是:为每一个agent的sequencer创建一个top_sequence,在test::run_phase中驱动

在这里插入图片描述
看起来挺好哈。那么问题又来了,每个agent的top_sequence之间的关系一定是fork…join嘛?如果我想先启动某几个agent,再全部启动呢?

所以说干脆啊,在test::run_phase()中直接一句dut_top_sequence.start(dut_top_sequencer);得了。其中dut_top_sequence是对每个agent的top_sequence进行控制,dut_top_sequencer中包含了每个agent的sequencer,这就是virtual sequence。

3. virtual sequence

virtual sequence其实是包含所有sequence成员的一个总体hierarchical sequence,它要挂载的virtual sequencer也是包含全部sequence成员的总体sequencer。

如下图所示,黑线表示嵌套

在这里插入图片描述

这样test的作用就只是创建env、配置env,挂载sequence。至于怎么产生trans发送给各sequencer,就是sequence的任务了。

3.1. 例子

如下所示,base_test需要配一个base_virtual_sequence,继承的test也配了一个继承的virtual_sequence。

但base_test必不会作为run_test()的测试用例,所以它无需挂载base_virtual_sequence。

在这里插入图片描述

class virtual_sequencer extends uvm_sequencer; chnl_sequencer chnl_sqr[3];reg_sequencer reg_sqr;fmt_sequencer fmt_sqr;//...
endclass
class env extends uvm_env;virtual_sequencer v_sqr;//...function void build_phase(uvm_phase phase);super.build_phase(phase);v_sqr = virtual_sequencer::type_id::create("v_sqr",this);//...endfunctionfunction void connect_phase(uvm_phase phase);foreach(v_sqr.chnl_sqr[i])v_sqr.chnl_sqr[i] = chnl_agts[i].sqr;v_sqr.reg_sqr = reg_agt.sqr;v_sqr.fmt_sqr = fmt_agt.sqr;//...endfunction
endclass

class base_test extends uvm_test;env e;//...task run_phase(uvm_phase phase);phase.raise_objection(this);run_virtual_sequence();phase.drop_objection(this);endtaskvirtual task run_virtual_sequence();endtask
endclass
class base_test_virtual_sequence extends uvm_sequence;//...`uvm_declare_p_sequencer(virtual_sequencer)chnl_sequence chnl_seq[3];reg_sequence reg_seq;fmt_sequence fmt_seq;task body();do_reg();do_fmt();do_data();endtaskvirtual task do_reg();endtaskvirtual task do_fmt();endtaskvirtual task do_data();endtask
endclass

class my_test extends base_test;//...task run_virtual_sequence();base_virtual_sequence seq;seq = base_virtual_sequence::type_id::create("seq");seq.start(env.v_sqr);endtask
endclass
class my_virtual_sequence extends base_virtual_sequence;//...task do_reg();`uvm_do_on_with(reg_seq,this.p_sequencer.reg_sqr,{cmd == `WRITE;addr == 'h000AC;})//...endtask
endclass

在实际调试中,可能存在多个flat sequence、virtual sequence和test,它们相互之间有着多重继承or重载的关系,调试起来可能比较麻烦,所以个人认为属于一条继承分支的联合在一起看就好
例如上述代码中的base_testdata_consistence_base_test就可以一起看,base_virtual_sequencedata_consistence_base_virtual_sequence一起看


3.2. hierarchical virtual sequence

如果一个sequencer有好几个sequence可用于驱动呢?

例如,对于reg_sequencer,有uvm_reg_access_seq、uvm_reg_bit_bash_seq等等一堆sequence,把这些sequence跟其他的fmt_sequence之类的全都堆在virtual_sequence里,就会显得非常冗杂。

所以在virtual_sequence内再放一个reg_virtual_sequence,不是用于驱动sequencer,就是为了对多个reg sequence进行控制

在这里插入图片描述

例子

下面这个例子说明了几个点:

每一个版本的test,都对应一个版本的全部virtual sequence

对于同一个版本的virtual sequence,低层级的virtual sequence要在高层级的virtual sequence的body()内例化、挂载

在这里插入图片描述

class base_test extends uvm_test;env e;//...task run_phase(uvm_phase phase);phase.raise_objection(this);run_virtual_sequence();phase.drop_objection(this);endtaskvirtual task run_virtual_sequence();endtask
endclassclass base_virtual_sequence extends uvm_sequence;//...fmt_sequence fmt_seq;reg_base_virtual_seuqence reg_base_seq;`uvm_declare_p_sequencer(virtual_sequencer)chnl_sequence chnl_seq[3];reg_virtual_sequence reg_vseq;fmt_sequence fmt_seq;task body();do_reg();do_fmt();do_data();endtaskvirtual task do_reg();endtaskvirtual task do_fmt();endtaskvirtual task do_data();endtask
endclassclass reg_base_virtual_sequence extends uvm_sequence;reg_block rgm;reg_adapter adapter;uvm_reg_hw_reset_seq reg_rst_seq; uvm_reg_bit_bash_seq reg_bit_bash_seq;uvm_reg_access_seq reg_acc_seq;virtual task body();endtask
endclass

class my_test extends base_test;//...task run_virtual_sequence();my_virtual_sequence seq;seq = my_virtual_sequence::type_id::create("seq");seq.start(env.v_sqr);endtask
endclassclass my_virtual_sequence extends base_virtual_sequence;//...reg_my_virtual_sequence seq;task do_reg();seq = reg_my_virtual_sequence::type_id::create("seq");seq.start_item(this.p_sequencer.reg_sqr,this);endtasktask do_fmt();`uvm_do_on_with(fmt_seq,this.p_sequencer.fmt_sqr)endtask//...
endclassclass reg_my_virtual_sequence extends reg_base_virtual_sequence;task body();//...endtask
endclass

class full_test extends base_test;//...task run_virtual_sequence();full_virtual_sequence seq;seq = full_virtual_sequence::type_id::create("seq");seq.start(env.v_sqr);endtask
endclassclass full_virtual_sequence extends base_virtual_sequence;//...reg_full_virtual_sequence seq;task do_reg();seq = reg_full_virtual_sequence::type_id::create("seq");seq.start_item(this.p_sequencer.reg_sqr,this);endtask//...
endclassclass reg_full_virtual_sequence extends reg_base_virtual_sequence;task body();//...endtask
endclass

4. layer sequence

driver向dut驱动时,如果要驱动的数据项过多,要加的约束过于庞大时,只通过一个trans或flat sequence罗列一大堆随机变量和constraint,就非常冗杂。

4.1. 问题阐述

例如串行RapidIO(Serial RapidIO,SRIO)的请求数据包就长这个样子的

在这里插入图片描述
可以看到内容很多,而且分成三级规范体系结构:逻辑层、传输层和物理层,层层嵌套,最终driver要驱动的就是Physical layer的全部内容。

如果这些内容都罗列在一个trans类中,每一项的constraint也都堆在这个trans类中,那就太庞杂了。

class transaction extends uvm_sequence_item;rand bit S;rand bit [2:0] AckID;rand bit Crf;rand bit [1:0] Prio;//...rand bit [15:0] CRC;constraint cstr{...}//...
endclass

不仅如此,在OSI(Open System Interconnect)模型中,网络结构分为7层:应用层、表示层、会话层、传输层、
网络层、链路层、物理层,每层都有着相应的协议与数据结构。

常见的PCIe、USB、UFS等标准都是分层传输协议,相应的验证IP也常采用分层验证环境,方便扩展和复用。

4.2. 每个layer 对应一个sequence

那怎么办呢?我们可以分成好几个transaction类嘛

class phy_trans extends uvm_sequence_item;rand bit S;rand bit [2:0] AckID;rand bit Crf;rand bit [1:0] Prio;rand bit [182:0] log_trp_payload; //逻辑层和传输层的位置//...
endclassclass trp_trans extends uvm_sequence_item;rand bit [1:0] TT;//...rand bit [143:0] log_payload; //逻辑层的位置//...
endclassclass log_trans extends uvm_sequence_item;rand bit [3:0] Ttype;rand bit [3:0] Wrsize;//...
endclass

而对于sequence,也是每个trans对应一个sequence,那么问题来了,如何将这些sequence组织在一起呢?

在这里插入图片描述
思路是这样的:首先我们必须有一个sequencer与driver建立TLM连接,然后phy_seq产生一个合适的phy_trans再`uvm_do_with。那么我们就需要填补phy_trans中log_trp_payload的部分,而这一部分由trp_trans组成。而trp_trans是由trp_seq产生,那么问题来了,如何将trp_trans放入到phy_trans中呢?

方法是,学着driver与sequence的样子,为trp_seq设立一个trp_sequencer,为phy_seq设立一个phy_sequencer,将两个sequencer实现TLM连接。然后在phy_seq的body()内通过p_sequencer和uvm_seq_item_pull_port获取trp_trans。trp_trans获取log_trans的方法同上。

对于monitor来说也可按照层级划分monitor

见下图

在这里插入图片描述

代码实现起来就是个迭代的流程

class phy_sequencer extends uvm_sequencer#(phy_trans);uvm_seq_item_pull_port#(trp_trans) seq_trp_trans_port;//uvm_seq_item_pull_imp #(phy_trans,phy_sequencer) seq_item_export; //uvm_sequencer already has//...
endclassclass trp_sequencer extends uvm_sequencer#(trp_trans);uvm_seq_item_pull_port#(log_trans) seq_log_trans_port;//uvm_seq_item_pull_imp #(trp_trans,trp_sequencer) seq_item_export; //uvm_sequencer already has//...
endclassclass log_sequencer extends uvm_sequencer#(log_trans);//uvm_seq_item_pull_imp #(log_trans,log_sequencer) seq_item_export; //uvm_sequencer already has//...
endclassclass agent extends uvm_agent;//...function void connect_phase(uvm_phase phase);driver.seq_item_port.connect(phy_sqr.seq_item_export);phy_sqr.seq_trp_trans_port.connect(trp_sqr.seq_item_export);trp_sqr.seq_log_trans_port.connect(log_sqr.seq_item_export);phy_mon.phy_collected_port.connect(trp_mon.phy_collected_export);trp_mon.trp_collected_port.connect(log_mon.trp_collected_export);//...endfunction
endfunction

class log_sequence extends uvm_sequence#(log_trans);task body();//...endtask
endclassclass trp_sequence extends uvm_sequence#(trp_trans);`uvm_declare_p_sequencer(trp_sequencer)//...task body();trp_trans trp_req;log_trans log_req;forever begin //forever`uvm_rand(trp_req)p_sequencer.seq_log_trans_port.get_next_item(log_req);trp_req = this.put_into_req(log_req);`uvm_send(trp_req)p_sequencer.seq_log_trans_port.item_done();endendtask
endclassclass phy_sequence extends uvm_sequence#(phy_trans);`uvm_declare_p_sequencer(phy_sequencer)//...task body();phy_trans phy_req;trp_trans trp_req;forever begin //forever`uvm_rand(phy_req)p_sequencer.seq_trp_trans_port.get_next_item(trp_req);phy_req = this.put_into_req(trp_req);`uvm_send(phy_req)p_sequencer.seq_trp_trans_port.item_done();endendtask
endclass


class phy_monitor extends uvm_monitor;uvm_analysis_port#(phy_trans) phy_collected_port;//...
endclassclass trp_monitor extends uvm_monitor;uvm_analysis_imp#(phy_trans,trp_monitor) phy_collected_export;uvm_analysis_port#(trp_trans) trp_collected_port;//...no run phasefunction void write(phy_trans phy_t);trp_trans trp_t;trp_t = this.extract_trp_trans(phy_t);trp_collected_port.write(trp_t);endfunction
endclassclass log_monitor extends uvm_monitor;uvm_analysis_imp#(trp_trans,log_monitor) trp_collected_export;uvm_analysis_port#(log_trans) log_collected_port;//...no run phasefunction void write(trp_trans trp_t);log_trans log_t;log_t = this.extract_log_trans(trp_t);log_collected_port.write(log_t);endfunction
endclass

class virtual_sequencer extends uvm_sequencer;log_sequencer log_sqr;trp_sequencer trp_sqr;phy_sequencer phy_sqr;//...
endclassclass virtual_sequence extends uvm_sequence;//...log_sequence log_seq;trp_sequence trp_seq;phy_sequence phy_seq;`uvm_declare_p_sequencer(virtual_sequencer)task body();fork`uvm_do_on(log_seq,p_sequencer.log_sqr);`uvm_do_on(trp_seq,p_sequencer.trp_sqr);`uvm_do_on(phy_seq,p_sequencer.phy_sqr);join_none //这里是fork...join_none
endclassclass test extends uvm_test;//...task run_phase(uvm_phase phase);phase.raise_objection(this);virtual_sequence seq;seq = virtual_sequence::type_id::create("seq");seq.start(env.v_sqr);phase.drop_objection(this);endtask
endclass

由于每产生一个log_seq,都要将它填补到trp_seq,进而填补到log_seq,所以trp_seq::body()phy_seq::body()都是forever。

所以在virtual_sequence::body()中三者必须是fork…join_none的关系,否则就会一直运行下去。

5. 本质:`uvm_declare_p_sequencer(my_sequencer)

无论是virtual sequence还是layer sequence,之所以能玩的这么花,其核心在于p_sequencer。

有了p_sequencer,sequence就可以访问它所挂载的sequencer。

例如 virtual sequence中的sequence就可以通过p_sequencer访问virtual sequencer中的其他sequencer

layer sequence中的p_sequencer就可以访问挂载的sequencer的端口。

其实是因为sequencer作为component,啥都可以有,跟谁都能打交道,sequence借助p_sequencer访问其他的component

只可在body()内使用p_sequencer

先看下面这个代码

class sequence extends uvm_sequence;//...`uvm_declare_p_sequencer(sequencer)local reg_model rgm = p_sequencer.rgm;//...
endclass
task test::run_phase(uvm_phase phase);//...sequence seq = new(); //这一步例化seq,会对seq.rgm产生什么操作?seq.start(env.sqr);
endtask

会报错,因为在执行sequence seq = new();的时候,会执行local reg_model rgm = p_sequencer.rgm;,那么问题来了这个时候有没有p_sequencer?

没有,因为只有在seq.start(env.sqr);才挂载sqr,挂载之前去哪找p_sequencer啊?

5.1. 心跳

class heartbeat_sequence extends uvm_sequence#(trans);`uvm_declare_p_sequencer(sequencer)//...task body();trans t;forever beginrepeat(10) @(posedge p_sequencer.vif.clk);`uvm_do(t)endendtask
endclassclass sequencer extends uvm_sequencer;local chnl_intf vif;//...
endclassclass virtual_sequence extends uvm_sequence;//...`uvm_declare_p_sequencer(virtual_sequencer)heartbeat_sequence hb_seq;normal_sequence nor_seq;task body();fork`uvm_do_on(hb_seq,p_sequencer.sequencer)`uvm_do_on(nor_seq,p_sequencer.sequencer)join_noneendtask
endclass


推荐阅读
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
author-avatar
mobiledu2502868835
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有