SV核心知识点-1

SV 

 1.数据类型

结构体
struct packed{
logic [7:0]a;
logic [7:0]b;
logic [7:0]c;
}pix
function void display(logic [2:0][7:0]pix);//注意合并数组pix[2]表示的是结构体中的a
//如果上面去掉packed,那么pix[2]表示的是c,结构体默认是非合并数组

typedef struct packed{
bit [7:0]a;
bit [7:0]b;
bit [7:0]c;
}pix;
function void display(pix a);

队列
int q[$];
q = {1,2,3};
pop_front pop_back拿出来的是数
push_front(i) push_back(i)放进去的也是数

动态数组
int q[];
q = new[5]
q = '{1,2,3,4,5}

关联数组

数组的方法(适用于队列、动态数组、关联数组)
q=d.sum
q=d.sum with(int'(item>1))
q=d.sum with((item>1)?item:0)  sum函数返回的是int类型值(q是int类型)

tq=d.find with(item ==2)
tq=d.find_index with(item>2)
tq=d.find_first with(item == 2)
tq=d.find_first_index with(item>2)  find返回的是队列(tq是队列)

d.rsort()
d.sort()
d.reverse()
d.shuffle()

合并数组与非合并数组
合并数组用{},非合并数组用'{}


 2.随机化

1.关于约束
class item ;
  rand x;
  rand y;
  int z;
  
constraint cstr_0{
  x == 0 -> y = 0;
  solve x before y;//约束中增加一些新的点。这里是确定了约束的前后顺序
}

constraint cstr_1{  //约束中增加条件
  if (z=0) x = 0;
  else if(z=1) y=0;
}

constraint cstr_2{
  x inside {[2:5]};
}

endclass

item it;
it = new();
assert(it.randomize()with{x>0;y>0;});//注意格式
x = $urandom_range(3,10);//注意格式
it.cstr.constraint_mode(0);//用于产生一些错误的数据,测试错误信号

2.关于post_randomize函数
function void post_randomize();
		int i;
		haddr_q[0] 		= haddr;
		htrans_q[0]		= NSEQ;
		hrwdata_q[0] 	= hrwdata;//这三个是第一次随机化的值
		for (i = 1; i < bst_beats; i++) begin
			haddr_q[i] 	= haddr_q[i-1] + (2**hsize);//在随机化的值的基础上做一些规律性的变化
			htrans_q[i]	= SEQ; 
		end
endfunction
除了上述用在总线协议的地址信息、数据信息以外,还可以产生校验位,也就是在post_randomize函数中添加一个奇偶校验的函数,在每次数据随机化结束后,会自动调用这个函数产生校验位。

3.线程的使用

核心:线程间的同步使用event,两个线程访问同一个资源使用semphore,线程间需要传递信息使用event

(1)线程间的同步
module syn;
class data_transfer;
        event send_single;
        event receive_single;
        task send_data();
            @ send_single;
            $display("the data send is waited");
            -> receive_single;
            $display("the data receive is trigged");
        endtask

        task receive_data();

            -> send_single;
            $display("the data send is triggered");
            @ receive_single;
            $display("the data received is waited");
        endtask
        
        task run();
          fork
            send_data();
            receive_data();
          join
        endtask
    endclass


    initial begin
       data_transfer t;
       t = new();
       t.run();

    end
endmodule
(2)数据发送的关闭,event当对象使用
program dis_and__object;
class  do_event;
    event a,b,c,full;
    mailbox d,e,f;

    task do_event;
      fork:send_data_ctrl
        begin
            @a;
            $display("event a is waited");
            send_data(d,"d");
        end
        begin
            @b;
            $display("event b is waited");
            send_data(e,"e");
        end
        begin
            @c;
            $display("event c is waited");
            send_data(f,"f");
        end
      join_none

      @ full;
      $display("data send will disable");
      disable send_data_ctrl;
      $display("data send is disable");

    endtask

    task trigger_event;
        fork
        -> a;
        $display("event a is triggered");
        -> b;
        $display("event b is triggered");
        -> c;
        $display("event c is triggered");
        join
    endtask

    task send_data(mailbox m,string name = "m");
      int trans = 0;
      int cnt = 0;
      int i = $urandom_range(1,5);
      $display("the room of mailbox %s is %d",name,i);
      m = new(i);
      fork 
      forever begin
      #1;
      $display("the mailbox is %s, the cnt is %d",name,cnt);
        m.put(trans);
        trans = trans +1;
        cnt = cnt+1;
      end
      join_none

      wait (cnt == i);
      $display(" mailbox %s is full ",name);
      -> full;
    endtask
endclass

initial begin
    do_event e;
    e = new();
    fork
      e.do_event;
      e.trigger_event;
    join
end

endprogram

(3)关于信号间的握手
program find_max;
  class generator;
    bit [7:0]req = 0;
    bit [7:0]rsp;

    mailbox g_item;
    mailbox d_item;


    function new();
      req = 0;
      g_item = new();
      d_item = new();
    endfunction

    task send();
      repeat(5)begin
        #10 req = req+1;
        g_item.put(req);
        $display($time,"the req is put,the value is %0d",req);
        d_item.get(rsp);
        $display($time,"the rsp is get,the value is %0d",rsp);
      end
      endtask
  endclass

  class driver;
    bit [7:0]req;
    bit [7:0]rsp;
    
    mailbox g_item;
    mailbox d_item;



    task receive();
      repeat(5)begin
        g_item.get(req);
        $display($time ,"the req is get,the value is %0d",req);
        rsp = req;
        d_item.put(rsp);
        $display($time ,"the rsp is put,the value is %0d",rsp);
      end
    endtask


  endclass


  initial begin
  generator g;
  driver d;
  g = new();
  d = new();
  d.g_item = g.g_item;
  d.d_item = g.d_item;
  
    fork
      g.send();
      d.receive();

    join
    $finish();

  end

(4)多master访问一个slave,使用线程进行仲裁
program arbiter; 
    mailbox a;
    mailbox b;
    mailbox c;
    mailbox d;
    semaphore e;
    
    class rand_addr;
    randc bit [1:0]addr_a;
    endclass

    class default_master ;
      int i;
      bit[1:0] default_address;
      task initial_address();
          default_address = $urandom_range(0,3);
          $display("default address is %0d",default_address);
      endtask
    endclass

    class master_1 ;
      bit [1:0]master_1_address;
      task send_a(bit [1:0]addr,bit[1:0]addr_a);;
        $display("the address of master_1 is %0d",addr_a);
        master_1_address = addr_a;
          e.get();
          if(master_1_address != addr)
            e.put();
          else begin
            $display("the powder is driver by master_1");
            a.put(master_1_address);
            e.put();
            $display("master_1 release the powder");
        end
      endtask
    endclass

    class master_2 ;
      bit [1:0]master_2_address;
        task send_b(bit [1:0]addr,bit [1:0]addr_a);
          $display("the address of master_2 is %0d",addr_a);
          master_2_address = addr_a;
          e.get();
          if(master_2_address != addr)
            e.put();
          else begin
            $display("the powder is driver by master_2");
            b.put(master_2_address);
            e.put();
            $display("master_2 release the powder");
          end
      endtask
    endclass
    
    class master_3 extends rand_addr;
      bit [1:0]master_3_address;
      task send_c(bit [1:0]addr,bit[1:0]addr_a);
        $display("the address of master_3 is %0d",addr_a);
        master_3_address = addr_a;
          e.get();
          if(master_3_address != addr)
            e.put();
          else begin
            $display("the powder is driver by master_3");
            c.put(master_3_address);
            e.put();
            $display("master_3 release the powder");
          end
      endtask
    endclass

    class master_4 extends rand_addr;
      bit [1:0]master_4_address;
      task send_d(bit [1:0]addr,bit [1:0]addr_a);
        $display("the address of master_4 is %0d",addr_a);
        master_4_address = addr_a;
          e.get();
          if(master_4_address != addr)
            e.put();
          else begin
            $display("the powder is driver by master_4");
            d.put(master_4_address);
            e.put();
            $display("master_4 release the powder");
          end
      endtask
    endclass

    class slave;
        bit [1:0]addr;
        task accept();
        fork
        begin
          if(a.try_get(addr))
            $display("slave is accepted data from master1,%0d",addr);
        end
        begin
          if(b.try_get(addr))
            $display("slave is accepted data from master2,%0d",addr);
        end
        begin
          if(c.try_get(addr))
            $display("slave is accepted data from master3,%0d",addr);
        end
        begin
          if(d.try_get(addr))
            $display("slave is accepted data from master4,%0d",addr);
        end
        join
      endtask
    endclass
      


    class test;
      rand_addr ra;
      default_master dm;
      master_1 m1;
      master_2 m2;
      master_3 m3;
      master_4 m4;
      slave s;
      function new();
        ra = new();
        a = new();
        b = new();
        c = new();
        d = new();
        e = new(1);
        dm = new();
        m1 = new();
        m2 = new();
        m3 = new();
        m4 = new();
        s = new();
     endfunction
     task run();
        dm.initial_address();
        fork
          begin
            assert(ra.randomize());
            m1.send_a(dm.default_address,ra.addr_a);
          end
          begin
            assert(ra.randomize());
            m2.send_b(dm.default_address,ra.addr_a);
          end
          begin
            assert(ra.randomize());
            m3.send_c(dm.default_address,ra.addr_a);
          end
          begin
            assert(ra.randomize());
            m4.send_d(dm.default_address,ra.addr_a);
          end
        join
        s.accept();
      endtask
  endclass

  initial begin
    test t;
    t = new();
    repeat(5)
    t.run();
  end


endprogram

4.SV和UVM中对象的创建的区别:(1)在SV中new函数中不仅仅要进行new操作,而且还要进行对象的连接,也就是对象的连接和创建并不是完全剥离的,那么可能会出现在索引句柄的时候句柄为空的情况(2)在UVM中,由于层次化的机制出现,build phase和connect phase彼此相互独立,就会使得这个问题很好的解决(3)覆盖机制

UVM

TLM通讯
class master;
	uvm_analysis_port #(lvc_apb_transfer)item_collected_port

	function new();
	item_collected_port = new("item_collected_port",this);
	endfunction

  endclass

  class slave;
		uvm_analysis_port #(lvc_i2c_slave_transaction)xact_observed_port
  endclass

	`uvm_analysis_imp_decl(_apb_master)
	`uvm_analysis_imp_decl(_i2c_slave)
  class scoreboard;
	uvm_analysis_imp_apb_master #(lvc_apb_transfer,scoreboard) apb_trans_observe_imp;
	uvm_analysis_imp_i2c_slave #(lvc_i2c_slave_transaction,scoreboard) i2c_trans_observe_imp;
	
	
	virtual function void write_apb_master();
	endfunction
	
	virtual function void write_i2c_slave();
	endfunction
  endclass
  
  class cgm;
	uvm_analysis_imp_apb_master #(lvc_apb_transfer,scoreboard) apb_trans_observe_imp;
	uvm_analysis_imp_i2c_slave #(lvc_i2c_slave_transaction,scoreboard) i2c_trans_observe_imp;
  endclass


	class env;
	
	virtual void connect_phase(uvm_phase phase);
		master.item_collected_port.connect(scoreboard.apb_trans_observe_imp);
		slave.xact_observed_port.connect(scoreboard.i2c_trans_observe_imp);
		
		master.item_collected_port.connect(cgm.apb_trans_observe_imp);
		slave.xact_observed_port.connect(cgm.i2c_trans_observe_imp);
	endfunction
	
	endclass
总结:一个port连接多个import是通过analysis_port解决的,但是一个import被多个port连接,是通过宏定义来解决方法重名的问题。
在这里,由于使用了analysis port,因此方法必然是write函数,宏定义后也是在write函数后加后缀。

1.关于回调函数的使用:

应用1:下图是正向的使用测试用例进行测试

class mcdf_data_consistence_basic_test extends mcdf_base_test;

    `uvm_component_utils(mcdf_data_consistence_basic_test)

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

    task do_reg();
      bit[31:0] wr_val, rd_val;
      // slv0 with len=8,  prio=0, en=1
      wr_val = (1<<3)+(0<<1)+1;
      this.write_reg(`SLV0_RW_ADDR, wr_val);
      this.read_reg(`SLV0_RW_ADDR, rd_val);
      void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));

      // slv1 with len=16, prio=1, en=1
      wr_val = (2<<3)+(1<<1)+1;
      this.write_reg(`SLV1_RW_ADDR, wr_val);
      this.read_reg(`SLV1_RW_ADDR, rd_val);
      void'(this.diff_value(wr_val, rd_val, "SLV1_WR_REG"));

      // slv2 with len=32, prio=2, en=1
      wr_val = (3<<3)+(2<<1)+1;
      this.write_reg(`SLV2_RW_ADDR, wr_val);
      this.read_reg(`SLV2_RW_ADDR, rd_val);
      void'(this.diff_value(wr_val, rd_val, "SLV2_WR_REG"));

      // send IDLE command
      this.idle_reg();
    endtask

    task do_formatter();
      void'(fmt_gen.randomize() with {fifo == LONG_FIFO; bandwidth == HIGH_WIDTH;});
      fmt_gen.start();
    endtask

    task do_data();
      void'(chnl_gens[0].randomize() with {ntrans==100; ch_id==0; data_nidles==0; pkt_nidles==1; data_size==8; });
      void'(chnl_gens[1].randomize() with {ntrans==100; ch_id==1; data_nidles==1; pkt_nidles==4; data_size==16;});
      void'(chnl_gens[2].randomize() with {ntrans==100; ch_id==2; data_nidles==2; pkt_nidles==8; data_size==32;});
      fork
        chnl_gens[0].start();
        chnl_gens[1].start();
        chnl_gens[2].start();
      join
      #10us; // wait until all data haven been transfered through MCDF
    endtask
  endclass: mcdf_data_consistence_basic_test


下图是使用回调的方式达到和前面的测试用例的相同办法。

class mcdf_base_test extends uvm_test;
`uvm_register_cb(mcdf_base_test, cb_mcdf_base)

task run_phase(uvm_phase phase);
     phase.raise_objection(this);
      this.do_reg();均是虚任务,每个任务中增加了`uvm_do_callbacks(mcdf_base_test, cb_mcdf_base, cb_do_reg()),以此来添加回调函数
      this.do_formatter();
      this.do_data();
      phase.drop_objection(this);
    endtask

endclass

定义一个具体的callback

class cb_mcdf_base extends uvm_callback;
    mcdf_base_test test;
  

    virtual task cb_do_reg();
     虚任务
    endtask

    virtual task cb_do_formatter();
     虚任务
    endtask

    virtual task cb_do_data();
     虚任务
    endtask

  endclass
class cb_mcdf_data_consistence_basic extends cb_mcdf_base;
    task cb_do_reg();
       放具体的内容
    endtask

    task cb_do_formatter();
     具体的内容
    endtask

    task cb_do_data();
      具体的内容
    endtask
  endclass: cb_mcdf_data_consistence_basic

 注意这部分test相当于一个容器的作用,在其中放了具体的回调函数。由于对于回调函数预留的入口在mcdf_base_test中,因此,在这里的绑定实际上也是将mcdf_base_test和具体例化的回调函数cb进行绑定。

class cb_mcdf_data_consistence_basic_test extends mcdf_base_test;
    cb_mcdf_data_consistence_basic cb;
    
    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      cb = cb_mcdf_data_consistence_basic::type_id::create("cb");
      uvm_callbacks#(mcdf_base_test)::add(this, cb);
    endfunction

    function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      cb.test = this;
    endfunction
  endclass: cb_mcdf_data_consistence_basic_test

回调函数关于pre的用法:
(1)在pre函数中定义相关条件,用于判断这个函数是否发生,典型的例子是uvm_event中的pre_trigger函数中的返回值,返回值为0继续执行相关trigger任务,如果返回值为1,不执行后续的内容。
(2)在发送数据前发送一些报文,用于快速定位数据产生的位置,也可以通过pre函数实现;对发送的数据产生一些约束,而具体的约束就可以通过pre函数定义。
启动sequence中的两种方式:(1)uvm_do_on_with(2)start函数,先进行new,一方面可以在new中给参数,另一方面在new之后可以通过句柄索引的方式赋参数。
package arbitration;
  import uvm_pkg::*;
  `include "uvm_macros.svh"
  class item extends uvm_sequence_item;
    `uvm_object_utils(item)
    rand int i;
    function new(string name = "item");
      super.new(name);
    endfunction
  endclass

  class seq extends uvm_sequence#(item);
    `uvm_object_utils(seq)
    int i;
    function new(string name = "seq");
      super.new(name);
    endfunction

    virtual task body();
      item req;
      //#10;
      repeat(5)begin
          //#5;
          `uvm_do_pri_with(req,1000,{i == local::i; })
          //`uvm_do_with(req,{i == local::i; })
          i = i+1;
          `uvm_info("seq",$sformatf("the value of i is %0d",i),UVM_LOW)
      end
      
    endtask
  endclass
  
  class seq2 extends uvm_sequence#(item);
    `uvm_object_utils(seq)
    int i;
    function new(string name = "seq2");
      super.new(name);
    endfunction

    virtual task body();
      item req;
       //#20;
      m_sequencer.grab(this);
      repeat(5)begin
         // #20;
      m_sequencer.grab(this);
          `uvm_do_pri_with(req,1,{i == local::i; })
          //`uvm_do_with(req,{i == local::i; })
          i = i+1;
          `uvm_info("seq2",$sformatf("the value of i is %0d",i),UVM_LOW)
      m_sequencer.ungrab(this);
      end
    endtask
  endclass



    class driver extends uvm_driver#(item);
      `uvm_component_utils(driver)
      function new(string name = "driver",uvm_component parent);
        super.new(name,parent);
      endfunction

      virtual task run_phase(uvm_phase phase);
      forever begin
        item rsp;
        rsp = new();
        seq_item_port.get_next_item(rsp);
        `uvm_info("driver",$sformatf("the value of i is %0d",rsp.i),UVM_LOW)
        seq_item_port.item_done();
      end
      endtask

    endclass

    class sequencer extends uvm_sequencer #(item);
      `uvm_component_utils(sequencer)
      function new(string name = "sequencer",uvm_component parent);
        super.new(name,parent);
      endfunction
    endclass

    class env extends uvm_env;
      `uvm_component_utils(env)
      sequencer sqr;
      driver dr;
      function new(string name = "env",uvm_component parent);
        super.new(name,parent);
      endfunction

      function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        sqr = sequencer::type_id::create("sqr",this);
        dr = driver::type_id::create("dr",this);
      endfunction

      function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        dr.seq_item_port.connect(sqr.seq_item_export);
      endfunction
    endclass

    class test extends uvm_test;
      `uvm_component_utils(test)
      env e;
      seq s1;
      seq2 s2;
      function new(string name = "parent",uvm_component parent);
        super.new(name,parent);
      endfunction
      function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        e = env::type_id::create("e",this);
        s1 = new("s1");
        s2 = new("s2");
      endfunction

      task run_phase(uvm_phase phase);
        phase.raise_objection(this);
        e.sqr.set_arbitration(UVM_SEQ_ARB_FIFO);
        fork
          s1.start(e.sqr,null,100);
          s2.start(e.sqr,null,1);
        join
        phase.drop_objection(this);
      endtask
    endclass

endpackage

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值