system verilog(降龙十八掌)

第一式:开启多个线程
SV——线程及线程间的通信(一) · 大专栏 (dazhuanlan.com)https://www.dazhuanlan.com/idadai/topics/1523517

initial begin
 语句1;
    #10;
  fork
        语句2;
       语句3;
   join_none
   语句4;
    语句5;
end

// #0 语句1
// #10 语句2,语句3,语句4并发执行
// #10 语句4执行完之后才执行语句5。4执行完之后,即使2,3没执行完,也会接着执行5,因为fork块内语句与之后的语句是并行的,不会阻塞之后的语句
// 来源:https://www.dazhuanlan.com/idadai/topics/1523517

如果循环启动线程,需要用 automatic 关键字,来自动创建变量,这样为每个线程单独分配内存。

initial begin
  for(int i=0;i<3;i++)
        fork
            automatic int k=i;//注意k的声明
         $display(k); // 注意这里用的式k
       join_none
end

wait fork 来等待线程都执行完。

需求:在某一个循环里面,根据条件来决定是否开启fork   join_none;但是可能需要把对应的条件打印出来。(注意automatic 的使用)


foreach (mab_is_1[i]) begin

    if(mab_is_1[i] == 1 ) begin
        int    search_range_window=0;
        fork
            automatic  int  interrupt_qid = i;
            do begin
                if ( ???) begin
                    `uvm_info(get_type_name(), $sformatf("Congratulation: interrupt compare is success!(Qid=%0d)",interrupt_qid), UVM_LOW);
                    break;
                end
                @(posedge v_xxx_intf.clk);
                search_range_window++;
            while(search_range_window <= 10) ;
        join_none

    end

end

线程间通信

​ 测试平台中所有的线程需要传递数据,可能多个线程同时要访问同一个数据,测试平台的代码是使得同一时间只有一个线程能访问。

@e1是边沿敏感的阻塞语句;wait(e1.triggered()) 是电平敏感的。

event done[N];// N是发生器数目
int cnt;
initial begin
  foreach (gen[i])begin
      gen[i]=new(done[i]);
        gen[i].run();
  end
 foreach(gen[i]) fork
       automatic int k=i;
        begin //begin块
            wait(done[k].triggered());
            cnt++; //触发一个,计数加一。
        end
  join_none
    wait(cnt==N); //等待计数到N。说明所有的fork执行完毕,所有的事件都触发
end

第二式:callback 机制(三步骤)

回调的概念:(参考公众号:芯片学堂)

回调函数概念:在上面的类比中,“制作身份证”是一个首先被执行的主函数(main function),填写信息表中取送方式一栏就是向主函数注册回调函数(register a callback function),而“邮寄”和“本人来取”其实就是被注册的回调函数(callback function),主函数在结束之前会调用回调函数,这个调用的地方叫做回调函数的hook。回调函数的接口通常是预先定义好的,比如CC总不能给主函数注册一个“由民警配送到我家”的回调函数吧。

SV的callback步骤

1、在需要回调的地方预留入口;(类似pre_callback、post_callback)

2、定义回调的类及其内部的回调函数;(virtual class;virtual task;)

3、例化和添加回调类的实例;(回调类 xx=new(); 添加到1的地方)

// 第一步:添加回调函数和hook
typedef enum {OKAY, EXOKAY, SLVERR, DECERR} resp_type;
class slave_driver;
resp_type resp;
// 这个就是回调函数,一般声明称virtual,方便override
// 在使用的时候是直接继承该类并重载定义回调函数 
virtual task update_resp; 
endtask

task send_response;
   std::randomize(resp) with {resp == OKAY;}; 
   update_resp();
  // 这行代码就是回调函数的hook
endtask

endclass

// 第二步:使用回调数
class err_inject extends slave_driver;
// 在子类中重新定义回调函数的实现 
 virtual task update resp;
   $display("Injecting SLVERR"); 
    resp = SLVERR;
 endtask 
endclass

// 第三步:使用回调数
program error_test;

slave env env;
err_inject err_driver;
initial begin
   // 例化环境组件 
   env =new();
   err driver =new();
   // 用err_driver覆盖slave_driver
   env.slv_driver = err_driver();
   // 仿真开始 
   env.run(); 
end
endprogram

UVM的callback

参考白皮书P288的9.1.4小节

callback的开发者

1、定义一个A类:class A extend uvm_callback回调函数要添加virtual(以便使用者重载)

如:virtual task pre_tran(xxx123 m_driver, ref transaction tr)

2、声明yigeA_pool类:typedef uvm_callback #(xxx123,A) A_pool

                第一个参数:xxx123:这个A_pool将会被xxx123这个类使用;

                第二个参数:A:这是一个A类型的pool;

3、在要预留callback函数|任务接口的类中调用uvm_register_cb宏:

class xxx123 extend uvm_driver #(transaction);
   // 在主函数中注册回调函数
   `uvm_register_cb(xxx123,A)

 4、在调用callback函数或任务接口的函数或任务中,使用宏uvm_do_callback来调用回调函数;

task xxx123::main_phase(uvm_phase phase)
//.........
`uvm_do_callback(xxx123,A,pre_tran(parameters))
//........
endtask

使用者:需要实现具体的callback函数

1、从A中派生一个类ex_A,并重载回调函数pre_tran;

2、在测试用例这一层的connect_phase中(要求就是在上面步骤4中使用回调函数之前的phase即可):把ex_A类实例化并加入到A_pool中。(A_pool::add(xx.a,my_cb))参考白皮书P290 代码清单9-13

下面是:芯片学堂给出的一个实例

// 第一步:添加和引用回调函数,回调基类定义和hook分开 
class driver_callback extends uvm_callback;

uvm object utils(driver callback)

function new(string name = "driver_callback");
  super.new(name); 
endfunction
// 定义回调函数
virtual task pre drive;
endtask 

virtual task post_drive; 
endtask 

endclass

// 回调函数在使用之前,需要先被注册到回调hook的object当中
//注册的时候可以使用UVM提供的宏来完成,并且在object中加hook 
class driver extends uvm_component;
    `uvm_component_utils(driver)

    // 这里就是对回调函数的注册,将回调基类和驱动类型关联起来
    `uvm_register_cb(driver, driver_callback)
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction

task run_phase(uvm phase phase);
//这里就是对两个回调函数的hook,实际上是从回调函数资源池里面找函数执行
// 函数索引是驱动类型+回调类型+回调函数名
`uvm_do_callbacks(driver, driver_callback, pre_drive()); 
 drive_pkt();
`uvm_do_callbacks(driver, driver_callback, post_drive()); 
endtask

task drive pkt();
`uvm_info("DRIVER", "Inside drive_pkt method", UVM_LOW); 
endtask 
endclass

// 第二步:实现回调函数
// 在上面SystemVerilog的例子中,实现回调函数是要继承整个driver
// 在UVM这个例子中,回调函数的声明和hook是分开的,因此在实现回调函数的时候
// 只要继承callback类就可以了
class user callback extends driver callback:
`uvm_object utils(user callback)

function new(string name = "user_callback")
super.new(name); 
endfunction

task pre_drive;
uvm_info("USER_CALLBACK", "Inside pre_drive method", UVM_LOW); 
endtask

task post drive;
uvm_info( "USER_CALLBACK", "Inside post_drive method", UVM_LOW) 
endtask 

endclass

// 第三步:使用回调函数
// 在UVM这个例子中,因为回调函数跟hook是分开的,在使用的时候也需要单独例化
//在SystemVerilog实现的例子中,则是只要例化driver就可以,这是个区别 
class user callback_test extends basic_test;
// 声明用户实现后的回调类
 user_callback callback_1;

uvm_component_utils(user_callback_test)

function new(string name = "user_callback_test", uvm_component parent);
super.new(name,parent); 
endfunction

function void build_phase(uvm_phase phase);
super.build_phase(phase);

// 回调类实例化
callback 1 = user callback::type id::create("callback 1", this);
// 给env的driver的回调函数资源池里添加回调对象
uvm callback #(driver, driver callback)::add(env.drv, callback_1);

endfunction

endclass

第三式:给函数传递参数和函数返回

第1个点:函数返回,task是不能声明类型的,只有function允许声明类型,如下图所示。

第2个点就是:

  1. function或task里面参数的方向如果没有显示声明的话,就是input;
  2. 如果第一个参数指明方向是output,而后面的参数没有指明方向的话,也会是output,沿用前面的方向属性;
  3. const ref 这个特性也会被后面的参数沿用,如果后面的参数没有指明方向属性的话,正确方式如下图;

 第3点:函数返回可以用return提前结束函数的执行;参数output类型可以直接用;(外面声明一下,传给函数处理,再引用这个参数时,是已经处理后的值了)


第四式:随机化

1、调用randomize可以只随机部分变量;

2、多个随机约束冲突后,工具不会进行随机化,此时给出的值一般都是0;

解决方案1:关闭部分约束或调整约束;(xx.constraint_mode(0):参考绿皮书P157,例子6.38)

3、在约束里面使用foreach

4、随机化二维数组

 上面截图来自iEEE手册

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值