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