目录

  • sequence的层次化
  • Hierarchical Sequence
  • Virtual Sequence
  • Layering Sequence

sequence的层次化

  • 水平复用而言,在MCDF各个子模块的验证环境中,它指的是如何利用已有资源,完成高效的激励场景创建
  • 垂直复用来看,它指的是在MCDF子系统验证中,可以完成结构复用和激励场景复用两个方面
  • 无论是水平复用还是垂直复用,激励场景的复用很大程度上取决于如何设计sequence,使得底层的sequence实现合理的粒度,帮助完成水平复用,进一步依托于底层激励场景,最终可以实现底层到高层的垂直复用。

Hierarchical Sequence

  • 验证MCDF的寄存器模块时,将SV验证环境进化到UVM环境之后,测试寄存器模块的场景可以将其拆分为:

    • 设置时钟和复位;
    • 测试通道1的控制寄存器和只读寄存器;
    • 测试通道2的控制寄存器和只读寄存器;
    • 测试通道3的控制寄存器和只读寄存器。
  • 在上面的测试场景拆解下的sequence需要挂载的都是reg_master_agent中的sequencer。给出一段代码用来说明底层sequence的设计和顶层hierarchical sequence对这些底层sequence的结构化使用:
typedef enum {CLKON, CLKOFF, RESET, WRREG, RDREG} cmd_t;
class bus_trans extends uvm_sequence_item;rand cmd_t cmd;rand int addr;rand int data;constraint cstr{soft addr == 'h0;soft data == 'h0;}...
endclass
class clk_rst_seq extends uvm_sequence;rand int freq;...task body();bus_trans req;`uvm_do_with(req, {cmd == CLKON; data == freq;})`uvm_do_with(req, {cmd == RESET;})endtask
endclass
class reg_test_seq extends uvm_sequence;rand int chnl;...task body();bus_trans req;//write and read test for WR register`uvm_do_with(req, {cmd == WRREG; addr == chnl * 'h4;})`uvm_do_with(req, {cmd == RDREG; addr == chnl * 'h4;})//read for the RD register`uvm_do_with(req, {cmd == RDREG; addr == chnl * 'h4 + 'h10;})endtask
endclass
class top_seq extends uvm_sequence;...task body();clk_rst_seq clkseq;reg_test_seq regseq0, regseq1, regseq2;//turn on clock with 150Mhz and assert reset`uvm_do_with(clkseq, {freq == 150;})//test the register of channel0`uvm_do_with(regseq0, {chnl == 0;})//test the register of channel1`uvm_do_with(regseq1, {chnl == 1;})//test the register of channel2`uvm_do_with(regseq2, {chnl == 2;})      endtask
endclass
class reg_master_sequencer extends uvm_sequencer;...
endclass
class reg_master_driver extends uvm_driver;...task run_phase(uvm_phase phase);REQ tmp;bus_trans req;forever beginseq_item_port.get_next_item(tmp);void'($cast(req, tmp));`uvm_info("DRV", $sformatf("got a item \n %s", req.sprint()), UVM_LOW)seq_item_port.item_done();endendtask
endclass
class reg_master_agent extends uvm_agent;reg_master_sequencer sqr;reg_master_driver drv;...function void build_phase(uvm_phase phase);sqr = reg_master_sequencer::type_id::create("sqr", this);drv = reg_master_driver::type_id::create("drv", this);endfunctionfunction void connect_phase(uvm_phase phase);drv.seq_item_port.connect(sqr.seq_item_export);endfunction
endclass

Hierarchical Sequence解析

  • item类bus_trans包含几个简单的域cmd、addr、data。在clk_rst_seqreg_test_seq这两个底层sequence在例化和传送item时,就通过随机化bus_trans中的域来实现不同的命令和数据内容。
  • 通过不同的数据内容的item,最终可实现不同测试目的。在top_seq中就通过对clk_rst_seqreg_test_seq进行组合和随机化赋值,最终实现完整的测试场景,即先打开时钟和完成复位,其后对寄存器模块中的寄存器完成读写测试。
  • 所以如果将它们作为底层sequence,或者称之为element sequencetop_seq作为一个更高层的协调sequence,它本身会容纳并对它们进行协调和随机限制,通过将这些element sequence进行有机调度,最终完成期望的测试场景。
  • 那么这样的top_seq就可以成为Hierarchical Sequence,它内部可以包含多个sequence和item,通过层层嵌套最终完成测试序列的合理切分。
  • 验证时有了粒度合适的element sequence,就更容易在这些设计好的”轮子“上实现验证的加速过程。而水平复用,就非常依赖于hierarchical sequence的实现。
  • 如何区别接下来讲到的virtual sequence,毕竟它们两者之间的共同点就是对于各个sequence的协调。它们的不同在于hierarchical sequence面对的对象是同一个sequencer,即hierarchical sequence也会挂载到sequencer上面,而virtual sequence内部不同sequence可以允许面向不同sequencer种类

Virtual Sequence

  • 伴随着底层模块的验证周期趋于尾声,在MCDF子系统验证环境集成过程中,完成了前期的结构垂直复用,就需要考虑如何复用各个模块的element sequence和hierarchical sequence。

  • 对于更上层的环境,顶层的测试序列要协调的不再只是面向一个sequencer的sequence群,而是要面向多个sequencer的sequence群。那么面向不同sequencer的sequence群在组织以后如何分别挂接到不同的sequencer上呢?

  • 之前介绍的sequence都是面向单一的sequencer,因此挂载也很简单,即通过uvm_sequence::start()挂载root sequence,而在内部的child sequence则可以通过宏uvm_do实现。

  • 如果将各模块环境的element sequence和hierarchical sequence都作为可复用的sequence资源,那么就需要一个可以容纳各个sequence的容器承载它们,同时也需要一个合适的routing sequencer来组织不同结构中的sequencer,这样的sequence和sequencer分别称之为virtual sequencevirtual sequencer

  • sequence和sequencer之间的区别在于:

    • virtual sequence可以承载不同目标sequencer的sequence群落,而组织协调这些sequence的方式则类似于高层次的hierarchical sequence。
    • virtual sequence一般只会挂载到virtual sequencer上。virtual sequencer与普通的sequencer相比,它们起到桥接其它sequencer的作用,即virtual sequencer是链接所有底层sequencer句柄的地方,是中心化的路由器
    • 同时virtual sequencer本身并不会传送item数据对象,因此virtual sequencer不需要与任何的driver进行TLM连接。所以需要在顶层的connect阶段,做好virtual sequencer中各个sequencer句柄与底层sequencer实体对象的一一对接,避免句柄悬空
  • 下例用来表示element sequence/hierarchical sequencevirtual sequence的关系,以及底层sequencer与virtual sequencer的联系,同时也说明virtual sequence与virtual sequencer的挂载方法:

class mcdf_normal_seq extends uvm_sequence;`uvm_object_utils(mcdf_normal_seq)`uvm_declare_p_sequencer(mcdf_virtual_sequencer)  //p_sequencer是一个uvm_sequencer的子类//该宏相当于定义了mcdf_virtual_sequencer p_sequencer//然后$(p_sequencer, m_sequencer)做了转换//完成了父类句柄到子类句柄的转换,然后就可以访问子类所有成员变量,进而实现路由功能...task body();clk_rst_seq clk_seq;reg_cfg_seq cfg_seq;data_trans_seq data_seq;fmt_slv_cfg_seq fmt_seq;//配置formatter slave agent`uvm_do_on(fmt_seq, p_sequencer.fmt_sqr)//打开时钟并完成复位`uvm_do_on(clk_seq, p_sequencer.cr_sqr)//配置MCDF寄存器`uvm_do_on(cfg_seq, p_sequencer.reg_sqr)//传递channel数据包fork`uvm_do_on(data_seq, p_sequencer.chnl_sqr0)`uvm_do_on(data_seq, p_sequencer.chnl_sqr1)`uvm_do_on(data_seq, p_sequencer.chnl_sqr2)joinendtask
endclass
class mcdf_virtual_sequencer extends uvm_sequencer;cr_master_sequencer cr_sqr;reg_master_sequencer reg_sqr;chnl_master_sequencer chnl_sqr0;chnl_master_sequencer chnl_sqr1;chnl_master_sequencer chnl_sqr2;fmt_slave_sequencer fmt_sqr;`uvm_component_utils(mcdf_virtual_sequencer)function new(string name, uvm_component parent);super.new(name, parent);endfunction
endclass
class mcdf_env extends uvm_env;cr_master_agent cr_agt;reg_master_agent reg_agt;chnl_master_agent chnl_agt0;chnl_master_agent chnl_agt1;chnl_master_agent chnl_agt2;fmt_slave_agent fmt_agt;mcdf_virtual_sequencer virt_sqr;`uvm_component_utils(mcdf_env)function new(string name, uvm_component parent);super.new(name, parent);endfunctionfunction void build_phase(uvm_phase phase);cr_agt = cr_master_agent::type_id::create("cr_agt", this);reg_agt = reg_master_agent::type_id::create("reg_agt", this);chnl_agt0 = chnl_master_agent::type_id::create("chnl_agt0", this);chnl_agt1 = chnl_master_agent::type_id::create("chnl_agt1", this);chnl_agt2 = chnl_master_agent::type_id::create("chnl_agt2", this);fmt_agt = fmt_slave_agent::type_id::create("fmt_agt", this);virt_sqr = mcdf_virtual_sequencer::type_id::create("virt_sqr", this);endfunctionfunction void connect_phase(uvm_phase phase);//virtual sequencer connection,but no any TLM connection with sequencersvirt_sqr.cr_sqr = cr_agt.sqr;virt_sqr.reg_sqr = reg_agt.sqr;virt_sqr.chnl_sqr = chnl_agt.sqr;virt_sqr.chnl_sqr = chnl_agt.sqr;virt_sqr.chnl_sqr = chnl_agt.sqr;virt_sqr.fmt_sqr = fmt_agt.sqr;endfunction
endclass
class test1 extends uvm_test;mcdf_env e;...task run_phase(uvm_phase phase);mcdf_normal_seq seq;phase.raise_objection(phase);seq = new();seq.start(e.virt_sqr);phase.drop_objection(phase);endtask
endclass
  • virtual sequence mcdf_normal_seq可以承载各个子模块环境的element sequence,而通过最后挂载的virtual sequencer mcdf_virtual_sequencer中的各个底层sequencer句柄,各个element sequence可以分别挂载到对应的底层sequencer上。
  • 尽管在最后test1中,将virtual sequence挂载到了virtual sequencer上面,但这种挂载的根本目的是提供给virtual sequence一个中心化的sequencer路由,而借助在virtual sequence mcdf_normal_seq中使用宏'uvm_declare_p_sequencer使virtual sequence可以使用声明后的成员变量p_sequencer(类型为mcdf_virtual_sequencer)进一步回溯到virtual sequencer内部的各个sequencer句柄
  • 在这里使用'uvm_declare_p_sequencer在后台新建一个p_sequencer类型,而将m_sequencer的默认变量(uvm_sequencer_base类型)通过动态转换,变为类型为mcdf_virtual_sequencerp_sequencer
  • 只要声明的挂载sequencer类型正确,可通过这个宏完成方便的类型转换,因此可通过p_sequencer索引到在mcdf_virtual_sequencer中声明的各个sequencer句柄。
  • 需理解virtual sequence的协调作用和路由作用,以及在顶层中需要完成virtual sequencer同底层sequencer的连接,并最终在test层实现virtual sequence挂载到virtual sequencer上。
  • 这种中心化的协调方式使得顶层环境在场景创建和激励控制方面更加得心应手,而且在代码后期维护中,测试场景的可读性也得到了提高。

Virtual Sequence建议

  • 一开始学习virtual sequence和virutal sequencer时容易出现编译错误和运行时句柄悬空的错误,还有一些概念上的偏差,这里给出一些建议:

    • 需要区分virtual sequence同其它普通sequence(element sequence、hierarchical sequence)。
    • 需要区分virtual sequencer同其它底层负责传送数据对象的sequencer
    • 在virtual sequence中记得使用宏'uvm_declare_p_sequencer创建正确类型的p_sequencer变量,方便接下来各个目标sequencer的索引。
    • 顶层环境中记得创建virtual sequencer,并且完成virtual sequencer中各个sequencer句柄与底层sequencer的跨层次链接

Layering Sequence

  • 如果在构建更加复杂的协议总线传输,例如PCIe、USB3.0等,那么通过一个单一的传输层次会对以后的激励复用、上层控制不那么友好。
  • 对于这种更深层次化的数据传输,实际中无论是VIP还是自开发的环境,都倾向于通过若干抽象层次的sequence群落来模拟协议层次。
  • 通过层次化的sequence可以分别构建transaction layer、transport layer和physical layer等从高抽象级到低抽象级的transaction转化。这种层次化的sequence构建方式,称之为layering sequence
  • 例如在进行寄存器级别的访问操作,其需要通过transport layer转化,最终映射为具体的总线传输。
  • 下面例码阐述layer sequence的核心,突出sequence层级转换的思想:
typedef enum {CLKON, CLKOFF, RESET, WRREG, RDREG} phy_cmd_t;
typedef enum {FREQ_LOW_TRANS, FREQ_MED_TRANS, FREQ_HIGH_TRANS} layer_cmd_t;class bus_trans extends uvm_sequence_item;rand phy_cmd_t cmd;rand int addr;rand int data;constraint cstr {soft addr == 'h0;soft data == 'h0;}...endclassclass packet_seq extends uvm_sequence;  //phy_seq:包含并生成bus_transrand int len;rand int addr;rand int data[];rand phy_cmd_t cmd;constraint cstr {soft len inside {[30:50]};soft addr[31:16] == 'hFF00;data.size() == len;}...task body();bus_trans req;foreach(data[i])`uvm_do_with(req, {cmd == local::cmd;addr == local::addr;data == local::data[i];})endtaskendclassclass layer_trans extends uvm_sequence_item;rand layer_cmd_t layer_cmd;rand int pkt_len;rand int pkt_idle;constraint cstr {soft pkt_len inside {[10:20]};layer_cmd == FREQ_LOW_TRANS -> pkt_idle inside {[300:400]};layer_cmd == FREQ_MED_TRANS -> pkt_idle inside {[100:200]};layer_cmd == FREQ_HIGH_TRANS -> pkt_idle inside {[20:40]};}...endclassclass adapter_seq extends uvm_sequence;`uvm_object_utils(adapter_seq)`uvm_declare_p_sequencer(phy_master_sequencer)...task body();layer_trans trans;packet_seq pkt;forever beginp_sequencer.up_sqr.get_next_item(req);  //获得高抽象层(layering_sequencer)的transactionvoid'($cast(trans, req));repeat(trans.pkt_len) begin //高抽象级长度转换为低抽象级的pkt`uvm_do(pkt)delay(trans.pkt_idle);endp_sequencer.up_sqr.item_done();endendtaskvirtual task delay(int delay);...endtaskendclassclass top_seq extends uvm_sequence;...task body();layer_trans trans;`uvm_do_with(trans, {layer_cmd == FREQ_LOW_TRANS;})`uvm_do_with(trans, {layer_cmd == FREQ_HIGH_TRANS;})endtaskendclassclass layering_sequencer extends uvm_sequencer;...endclassclass phy_master_sequencer extends uvm_sequencer;layering_sequencer up_sqr;  //notice...endclassclass phy_master_driver extends uvm_driver;...task run_phase(uvm_phase phase);REQ tmp;bus_trans req;forever beginseq_item_port.get_next_item(tmp);void'($cast(req, tmp));`uvm_info("DRV", $sformatf("got a item \n %s", req.sprint()), UVM_LOW)seq_item_port.item_done();endendtaskendclassclass phy_master_agent extends uvm_agent;phy_master_sequencer sqr;phy_master_driver drv;...function void build_phase(uvm_phase phase);sqr = phy_master_sequencer::type_id::create("sqr", this);drv = phy_master_driver::type_id::create("drv", this);endfunctionfunction void connect_phase(uvm_phase phase);drv.seq_item_port.connect(sqr.seq_item_export);endfunctionendclassclass test1 extends uvm_test;layering_sequencer layer_sqr;phy_master_agent phy_agt;...function void build_phase(uvm_phase phase);layer_sqr = layering_sequencer::type_id::create("layer_sqr", this);phy_agt = phy_master_agent::type_id::create("phy_agt", this);endfunctionfunction connect_phase(uvm_phase phase);phy_agt.sqr.up_sqr = layer_sqr;  //将高抽象级的sqr句柄传递到phy的sqr.up_sqr,才能调用up_sqr.get_next_itemendfunctiontask run_phase(uvm_phase phase);top_seq seq;adapter_seq adapter;phase.raise_objection(phase);seq = new();adapter = new();forkadapter.start(phy_agt.sqr);join_none  //不能没有fork_join_none,因为adapter的body()是foreverseq.start(layer_sqr);phase.drop_objection(phase);endtaskendclass
  • 通过上面的代码,可以得出一些关于如何实现sequence layer协议转换的方法:

    • 无论有多少抽象层次的transaction类定义,都应该有对应sequencer作为transaction的路由通道。例如layer_sequencer和phy_master_sequencer分别作为layer_trans和bus_trans的通道。
    • 各抽象级的sequencer中需要有相应的转换方法,将高层次的transaction从高层次的sequencer获取,继而转换为低层次的transaction,最终通过低层次的sequencer发送出去。例如adapter_seq负责从layer_sequencer获取layer_trans,再将其转换为phy_master_sequencer一侧对应的sequence或transaction,最后将其从phy_master_sequencer发送出去。
    • 这些adaption sequence应该运行在低层次的sequencer一侧,作为“永动”的sequence时刻做好服务准备,从高层次的sequencer获取transaction,通过转化将其从低层次的sequencer一侧送出。例如上面的test1中,adapter sequence通过adapter.start(phy_agt.sqr)挂载到低层次的sequencer,做好转换transaction并将其发送的准备。
    • 至于需要定义多少个层次的transaction item类,上面的例子仅仅是为了说明layer sequence的一般方法,对实际中的层次定义和transaction item定义,还需具体问题具体处理。

Layering Sequence解析

  • 可以看到各个sequence类对应的sequencer,同时也有sequence item发送和转换的方向。经过转化,最终高层次的transaction内容可以落实到低层次的protocol agentphysical interface上面。
  • 例码没有给出读回路的处理,即从physical interface穿过physical agent,最终抵达layering sequencer的通信。在实际中可通过相同路径返回response item实现;也可通过不同层次的monitor采集response transaction,最终通过monitor转化抽象级返回item的方式来实现。
  • 选择哪种反馈回路与底层agent反馈回路的实现方式有关,即如果原有方式通过driver返回response,那么建议继续在该反馈链条上进行从低级transaction到高级transaction的转化,如果原有方式通过monitor一侧返回response,那么也建议创建对应的高层次monitor,实现抽象层次转化。

UVM入门与进阶学习笔记16——sequencer和sequence(2)相关推荐

  1. UVM入门与进阶学习笔记17——寄存器模型(2)

    目录 寄存器模型集成 总线UVC的实现 总线UVC解析 MCDF寄存器设计代码 Adapter的实现 Adapter的集成 访问方式 前门访问 后门访问 前门与后门的比较 前门与后门的混合应用 寄存器 ...

  2. UVM入门与进阶学习笔记4——UVM仿真的开始与结束

    目录 UVM的编译和运行顺序 UVM仿真开始 UVM世界的"诞生" UVM仿真结束 UVM的编译和运行顺序 下图是UVM的编译运行顺序,非常重要的知识点! 在加载硬件模型调用仿真器 ...

  3. Python数据分析入门--NumPy进阶学习笔记

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 NumPy 进阶操作 1.NumPy dtype 层次结构 2.NumPy 高阶数组操作 2.1数组的重塑 2.2数组 ...

  4. UVM入门与进阶学习笔11——TLM通信(3)

    目录 通信管道 TLM FIFO Analysis Port Analysis TLM FIFO Request & Response通信管道 通信管道 TLM通信的实现方式的共同点在于都是端 ...

  5. opencv进阶学习笔记10:图像金字塔和图像梯度

    基础版笔记传送门: python3+opencv学习笔记汇总目录(适合基础入门学习) 进阶版笔记目录链接: python+opencv进阶版学习笔记目录(适合有一定基础) 图像金字塔 变小 变大 原理 ...

  6. 深度学习入门之PyTorch学习笔记:卷积神经网络

    深度学习入门之PyTorch学习笔记 绪论 1 深度学习介绍 2 深度学习框架 3 多层全连接网络 4 卷积神经网络 4.1 主要任务及起源 4.2 卷积神经网络的原理和结构 4.2.1 卷积层 1. ...

  7. opencv进阶学习笔记3:像素运算和图像亮度对比度调节

    基础版传送门: python3+opencv学习笔记汇总目录(适合基础入门学习) 进阶版目录: python+opencv进阶版学习笔记目录(适合有一定基础) 像素运算 要求两张图大小,以及格式(np ...

  8. opencv学习笔记16:梯度运算之scharr算子及其函数使用

    前文介绍了sobel算子 opencv学习笔记14:sobel算子及其函数使用 scharr算子理论 系数和sobel不一样,其他一样. scharr函数使用 dst=cv2.Scharr(src,d ...

  9. matlab入门之旅,MATLAB 入门之旅学习笔记

    MATLAB 入门之旅学习笔记 https://matlabacademy.mathworks.com/R2019a/cn/portal.html?course=gettingstarted 1.概述 ...

最新文章

  1. mysql having实例_Mysql必读mysql group by having 实例代码
  2. java中删除sqlite数据库语句_sqlite数据库的介绍与java操作sqlite的实例讲解
  3. boost::log::sources::severity_logger用法的测试程序
  4. 使用PWM实现语音播放
  5. java ftp批量下载_java ftp连接一次下载多个文件
  6. 吉首大学 问题 L: 小李子的老年生活
  7. c++取临时文件夹_电脑C盘不足?学会删除这5个文件夹,能节省10G空间
  8. (life)新的一年新的一页
  9. 解压tar.xz文件和tar.gz文件
  10. python 帮助教师_花10分钟写一个Python脚本,搞定了初中老师一下午的工作
  11. 访问控制权限和 ------java命名规范
  12. iterm2 配置安装rz sz
  13. 计算机科学与技术代码0812,一级学科代码及名称0812计算机科学与技术(2007年)本.doc...
  14. 学习一门技术不能浅尝遏止
  15. cocos2d音效设置
  16. 排版侠html怎么复制,排版侠微信编辑器使用方法教程
  17. ICDsoft主机半价优惠码推荐
  18. 广度优先搜索算法和深度优先搜索算法——关于路径搜索的问题解决
  19. 如何给房子定一个合理的售价
  20. source music play list 11-26 (edit 12-6 by clin003 )

热门文章

  1. Android项目:基于Android手机校园外卖订餐系统(计算机毕业设计)
  2. JavaWeb全面知识总结之呕血三天精心整理
  3. Observability:Data pipeline:Beats => Redis => Logstash => Elasticsearch
  4. pdf文件删除空白页技巧介绍
  5. web项目连接阿里云云数据库RDS-MySQL8.0
  6. 深圳IT外包公司名单汇总
  7. 传感器实验——LCD显示小车状态
  8. log日志:如何设置log级别、打印字体的颜色
  9. 我也可以很极地很阳光
  10. 数据挖掘期末复习01-02