AHB2项目虽然有不少错误,但是从整体架构上比较规范。
https://gitee.com/bai-mengwei/AHB2
RTL
可能由于只是一个VIP,只有一个interface。在interface里具有断言的写法。
比如:其中的disable iff等表示需要系统不在复位状态,即rst=1时进行断言。
property size7_addr_p; @(posedge HCLK) disable iff(!HRESETn) HSIZE == 7 |-> HADDR[6:0] == 0; endproperty SIZE7_ADDR_BOUD: assert property(size7_addr_p);
SIM
一共6个代码。在sim文件夹中,有Makefile文件。文件中用vcs先把一些文件夹添加为搜索路径,然后编译文件总的pkg、top。这两个文件都在env文件夹中。
ENV
1. 一共6个代码。env文件夹中,top是顶层用于调用run_test函数,然后调用对应的test_case.
2. 在调用前,实例化interface,include 总的pkg(ahb_test_pkg,在test目录下),而test_case也在test目录里
注意这里的top是module,而整个项目没有program。一般时候,用top进行实例化interface后,是将program再次实例化,然后整个验证在program里。
TEST
1. ahb_test_pkg是总的pkg,包括了各种svh文件,也包括了uvm的头部
`include uvm_macros.svh import uvm_pkg::*
2. ahb_test.svh包括了所有的test_case,继承了uvm_test并注册到工厂中,因此可以调用。
3. 在每个 test_case调用过程中,基本类定义了env、和env_cfg,以及各种各类的vseq。
vseq来自于env文件夹中。
vseq从本质上是普通的seq,但是它的行为是调用真实的seq(在 task body中实现。)
vseqr本质上也是普通的seqr,但是它的内部包括了各种seqr的声明。并在env中进行了实例化连接(vseqr被各种agent的seqr进行赋值)。
传输过程
传输
在test_case中的run_phase,创建一个vseq,然后启动。
当一个特定的vseq启动,并使用env下的vseqr作为发送装置。可惜该发送装置没有接drv,不能让vseq在task_body中调用start_item, uvm_do等。而是进行了一些赋值操作。
1.基类vseqr:
首先调用base_vseq,将env的vseqr中的内容分别取出(m_sequencer),并进行赋值。
class ahb_base_vseq extends uvm_sequence#(uvm_sequence_item); `uvm_object_utils(ahb_base_vseq) ahb_vseqr vseqr_h; ahb_mseqr mseqr_h; ahb_sseqr sseqr_h; reset_seqr reset_seqr_h; reset_seq reset_seq_h; set_seq set_seq_h; ahb_idle_mseq idle_mseq_h; ahb_incrx_mseq incrx_mseq_h; ahb_wrapx_mseq wrapx_mseq_h; ahb_crt_mseq crt_mseq_h; ahb_incrbusy_mseq incrbusy_mseq_h; ahb_reset_sseq reset_sseq_h; ahb_ready_sseq ready_sseq_h; ahb_err_sseq err_sseq_h; extern function new(string name = "ahb_base_vseq"); extern task body(); endclass: ahb_base_vseq //Constructor function ahb_base_vseq::new(string name = "ahb_base_vseq"); super.new(name); endfunction //Body task ahb_base_vseq::body(); if(!$cast(vseqr_h, m_sequencer)) begin `uvm_fatal(get_full_name(), "Virtual Sequencer cast failed!") end reset_seqr_h = vseqr_h.reset_seqr_h; mseqr_h = vseqr_h.mseqr_h; sseqr_h = vseqr_h.sseqr_h; endtask
2. 对应类vseqr:
然后通过子类,对实际的seq进行创建和启动。实际的seq需要使用其对应的实际的seqr,而这些seqr在agent中声明后,已经接入了agent对应的drv中了,因此可以发出去。
class ahb_reset_vseq extends ahb_base_vseq; `uvm_object_utils(ahb_reset_vseq) extern function new(string name = "ahb_reset_vseq"); extern task body(); endclass: ahb_reset_vseq //Constructor function ahb_reset_vseq::new(string name = "ahb_reset_vseq"); super.new(name); endfunction //Body task ahb_reset_vseq::body(); super.body(); reset_seq_h = reset_seq::type_id::create("reset_seq_h"); reset_sseq_h = ahb_reset_sseq::type_id::create("reset_sseq_h"); fork reset_sseq_h.start(sseqr_h); join_none reset_seq_h.start(reset_seqr_h); endtask
3. 对应的seq
就使用了创建、开始、随机化以及修改,发送。然后被agent对应的drv拿到,开始驱动。
class reset_seq extends reset_base_seq; `uvm_object_utils(reset_seq) extern function new(string name = "reset_seq"); extern task body(); endclass: reset_seq //Constructor function reset_seq::new(string name = "reset_seq"); super.new(name); endfunction //Body task reset_seq::body(); req = ahb_mxtn::type_id::create("req"); start_item(req); assert(req.randomize() with {burst_mode == SINGLE;}); req.reset = 0; finish_item(req); endtask
说明总结
(1.vseq是临时创建并启动的,它需要一个对应的seqr)
(2.这个seqr是一个vseqr,它不是让对应的seq发送到drv的,而是存储真实的seqr的,因此在vseq基类的task body中,没有send,而是进行赋值)
RESET_AGENT
1. 这里的seqr和seq就很普通了。seqr只有一个注册过程,seqs包括了几个对应的类。
2. agent中对drv和seqr进行了连接