参考链接:UVM 源码
driver 和 sequencer 握手机制一:get_next_item():
// transaction
class my_data extends uvm_sequence_item; // 创建自己的transaction一般是从uvm_sequence_item,而不是uvm_transaction
rand bit [7:0] data;
rand bit [7:0] addr;
// Rest of the class contents come here ...
endclass
// driver
class my_driver extends uvm_driver #(my_data); // my_driver被参数化为仅接受“my_data”类型的对象
`uvm_component_utils (my_driver)
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
// 1. driver用get_next_item()从sequencer拿到item
`uvm_info ("DRIVER", $sformatf ("Waiting for data from sequencer"), UVM_MEDIUM)
seq_item_port.get_next_item(req);
`uvm_info ("DRIVER", $sformatf ("Start driving tx addr=0x%0h data=0x%0h", req.addr, req.data), UVM_MEDIUM)
#20; // 2.假设driver把接收到的数据包发给dut需要20ns
// 3. driver 调用 item_done() 让 sequencer 知道 driver 发送完 item
`uvm_info ("DRIVER", $sformatf ("Finish driving tx addr=0x%0h data=0x%0h", req.addr, req.data), UVM_MEDIUM)
seq_item_port.item_done(); // driver完成对item的处理
endtask
endclass
// sequence
class my_sequence extends uvm_sequence #(my_data); // my_sequence被参数化为仅接受“my_data”类型的对象
`uvm_object_utils (my_sequence)
virtual task body();
// 1. 在 sequence body 中实例化item: create item
my_data tx = my_data::type_id::create("tx");
`uvm_info ("SEQ", $sformatf("About to call start_item"), UVM_MEDIUM)
// 2. 调用 start_item() ,将这个 item 发送给 driver
start_item(tx);
`uvm_info ("SEQ", $sformatf("start_item() fn call done"), UVM_MEDIUM)
// 3. 因为传递给driver的类句柄指向同一个对象,可以先create再随机? 后随机
tx.randomize();
`uvm_info ("SEQ", $sformatf("tx randomized with addr=0x%0h data=0x%0h", tx.addr, tx.data), UVM_MEDIUM)
// 4. 调用 finish_item ,以便sequence等待,直到驱driver让sequencer知道该item已完成
finish_item(tx);
`uvm_info ("SEQ", $sformatf("finish_item() fn call done"), UVM_MEDIUM)
endtask
endclass
Driver 和 Sequencer 应该在 agent 实例化,agent 在 env 中实例化,env 在 test 中实例化
// test
class base_test extends uvm_test;
my_driver m_drv0;
uvm_sequencer #(my_data) m_seqr0; // sequencer 被参数化为仅接受“my_data”类型的对象
my_sequence m_seq;
// 在 build_phase 用 create 实例化 sequencer/driver 组件
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
m_drv0 = my_driver::type_id::create ("m_drv0", this);
m_seqr0 = uvm_sequencer#(my_data)::type_id::create ("m_seqr0", this);
endfunction
// 在 connect_phase 连接 driver 的 port(seq_item_port) 到 sequencer 的 export(seq_item_export)
virtual function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
m_drv0.seq_item_port.connect (m_seqr0.seq_item_export);
endfunction
// 在run_phase中实例化sequence; 在sequencer上启动sequence
virtual task run_phase(uvm_phase phase);
m_seq = my_sequence::type_id::create("m_seq"); // 实例化sequence
phase.raise_objection(this);
m_seq.start(m_seqr0); // 在sequencer上启动sequence
phase.drop_objection(this);
endtask
endclass
driver 和 sequencer 握手机制二: get() and put():
// transaction item
class my_data extends uvm_sequence_item;
rand bit [7:0] data;
rand bit [7:0] addr;
// Rest of the class contents come here ...
endclass
// driver
class my_driver extends uvm_driver #(my_data); // driver 被参数化为仅接受“my_data”类型的对象
`uvm_component_utils (my_driver)
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
// 1.driver 使用get从sequencer拿到item,req是uvm_driver类的pre-defined变量
`uvm_info ("DRIVER", $sformatf ("Waiting for data from sequencer"), UVM_MEDIUM)
seq_item_port.get(req);
// 2.假设的driver处理该item需要10ns
uvm_info ("DRIVER", $sformatf ("Start driving tx addr=0x%0h data=0x%0h", req.addr, req.data), UVM_MEDIUM)
#20;
// 3. 假设 DUT 将一些读取数据返回给driver,这些数据需要发送回到sequence。 请注意,读取的数据被放入相同的request的对象句柄中
`uvm_info ("DRIVER", $sformatf ("#20 delay over, curr data=0x%0h", req.data), UVM_MEDIUM)
req.data = 8'hAA; // 假设driver从dut拿到的响应数据是8'hAA
// 4. driver调用put,并将响应数据发送回sequencer
`uvm_info ("DRIVER", $sformatf ("About to call put() with new data=0x%0h", req.data), UVM_MEDIUM)
seq_item_port.put(req); // driver发回响应数据给sequencer
`uvm_info ("DRIVER", $sformatf ("Finish driving tx addr=0x%0h data=0x%0h", req.addr, req.data), UVM_MEDIUM)
endtask
endclass
// sequence
class my_sequence extends uvm_sequence #(my_data); // sequence 被参数化为仅接受“my_data”类型的对象
virtual task body();
// 1. 实例化item
my_data tx = my_data::type_id::create("tx");
`uvm_info ("SEQ", $sformatf("About to call start_item"), UVM_MEDIUM)
// 2. sequence 启动item
start_item(tx);
`uvm_info ("SEQ", $sformatf("start_item() fn call done"), UVM_MEDIUM)
// 3. 对item后随机 (先create再随机?)
tx.randomize();
`uvm_info ("SEQ", $sformatf("tx randomized with addr=0x%0h data=0x%0h", tx.addr, tx.data), UVM_MEDIUM)
// 4. sequence 调用 finish_item
finish_item(tx);
`uvm_info ("SEQ", $sformatf("finish_item() fn call done, wait for rsp"), UVM_MEDIUM)
// 阻塞型get_response会等到driver发回数据响应
get_response(tx);
`uvm_info ("SEQ", $sformatf("get_response() fn call done rsp addr=0x%0h data=0x%0h, exit seq", tx.addr, tx.data), UVM_MEDIUM)
endtask
endclass
// test
class base_test extends uvm_test;
// sequencer 被参数化为仅接受“my_data”类型的对象
my_driver m_drv0;
uvm_sequencer #(my_data) m_seqr0; // 使用了uvm自带的uvm_sequencer类,而没有自定义uvm_sequencer子类
my_sequence m_seq;
// 在 build_phase 用 create 实例化 sequencer/driver 组件
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
m_drv0 = my_driver::type_id::create ("m_drv0", this);
m_seqr0 = uvm_sequencer#(my_data)::type_id::create ("m_seqr0", this);
endfunction
// 在 connect_phase 连接 driver 的 port(seq_item_port) 到 sequencer 的 export(seq_item_export)
virtual function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
m_drv0.seq_item_port.connect (m_seqr0.seq_item_export);
endfunction
// 在run_phase中实例化sequence; 在sequencer上启动sequence
virtual task run_phase(uvm_phase phase);
m_seq = my_sequence::type_id::create("m_seq"); // 在run phase实例化sequence
phase.raise_objection(this);
m_seq.start(m_seqr0);
phase.drop_objection(this);
endtask
endclass
driver seq_item_port TLM端口:
- seq_item_port.get(req)
- seq_item_port.put(req)
- seq_item_port.get_next_item(req)
- seq_item_port.item_done()
- 一个 seq_item_port 只能 connect 一个 seq_item_export
- 可以在一个 driver 中用数组的形式定义多个 uvm_seq_item_pull_port,用来连接多个 sequencer
get()和get_next_item()的区别在于get()中调用了item_done()函数,所以可以发现在上面的代码中,get到了transaction后并没有item_done()操作