In reply to AL_verif:
You can control it from the sequence by giving the constraints and you can make the driver as reusable
//sequence
task body()
req = item::type_id::create("req");
// By Cange the loop limit you can control the no.of data to be driven continuously
repeat(10)
begin
start_item(req);
assert(req.randomize() with{ valid == 1;});
finish_item(req);
end
// After driving data making valid as 0
start_item(req);
assert(req.randomize() with{ valid == 0;});
finish_item(req);
endtask
// Now in driver you need not to do any changes
virtual task main_phase(uvm_phase phase);
super.main_phase(phase);
forever begin
seq_item_port.get_next_item(req);
drive_data(reg);
seq_item_port.item_done();
end
endtask : main_phase
// --------------------------------------
virtual protected task drive_data(item curr_item);
// start transaction
@(posedge vif.clk);
vif.valid <= 1;
vif.data <= curr_item.data;
endtask:drive_data
valid is a controö signal you should never put it to the seq_item/transaction. But you could have 2 different operational modes, one for the standars and another one for the full band-width.
In reply to AL_verif:
If you don’t want to consider the control signal in the sequence item, you can do this way. In transaction class declare data as a dynamic array & constraint the size of the array from the sequence.
//sequence
task body()
req = item::type_id::create("req");
//for single transfer
begin
start_item(req);
assert(req.randomize() with{ data.size == 1;});
finish_item(req);
end
//for multiple transfersi.e.,full band width
start_item(req);
assert(req.randomize() with{data.size == 10/*or any other values per your requirement*/;});
finish_item(req);
endtask
// Now in driver you need not to do any changes
virtual task main_phase(uvm_phase phase);
super.main_phase(phase);
forever begin
seq_item_port.get_next_item(req);
drive_data(reg);
seq_item_port.item_done();
end
endtask : main_phase
// --------------------------------------
virtual protected task drive_data(item curr_item);
// start transaction
foreach(curr_item.data[i])
begin
@(posedge vif.clk);
vif.valid <= 1;
vif.data <= curr_item.data[i];
end
// release the bus
@ (posedge vif.clk);
vif.valid <= 0;
vif.data <= $random();
endtask:drive_data
but the issue is that I don’t have only “data” in my item, I have some fields.
and I don’t think it’s a good Idea to handle some queues or a queue of struct.
I believe that there is a simple solution to be done in driver.
we just need to find it.
task drv::run_phase(uvm_phase phase);
@(m_vif.drv_cb iff (m_vif.rst_n === 0));
// Reset signals
reset_signals();
@(m_vif.drv_cb iff (m_vif.rst_n === 1));
// Start driving
get_and_drive();
endtask : run_phase
task drv::get_and_drive();
process main_thread;
process rst_mon_thread;
forever begin
//Don't drive during reset
while(m_vif.rst_n !== 1)
@(m_vif.drv_cb iff (m_vif.rst_n === 1));
//Get the next item from the sequencer
seq_item_port.get_next_item(req);
if(!$cast(rsp, req.clone())) begin
`uvm_fatal(get_type_name(), "run_phase::Cast rsp<-req failed!")
end
rsp.set_id_info(req);
//Drive current transaction
fork
//Drive the transaction
begin
main_thread=process::self();
drive_item(rsp);
if(rst_mon_thread) rst_mon_thread.kill();
end
// Monitor the reset signal
begin
rst_mon_thread=process::self();
@(negedge m_vif.rst_n) begin
if(main_thread) main_thread.kill();
//Do reset
reset_signals();
end
end
join_any
//Send item_done and a response to the sequencer
seq_item_port.item_done();
seq_item_port.put_response(rsp);
end // forever
endtask : get_and_drive
task drv::drive_item(ref item_t item_h);
//Drive the req
if(item_h.m_trans_type == FIRST_TYPE) begin
automatic int delay_cycles = $urandom_range(m_cfg_h.min_dly,m_cfg_h.max_dly);
repeat (delay_cycles)
@(m_vif.drv_cb);
if(some_condition) begin
m_vif.drv_cb.One_Valid <= 1;
m_vif.drv_cb.One_Hdr <= item_h.m_hdr;
end
else begin
automatic item_t auto_item_h = item_h;
check_one_credits_and_drive(auto_item_h); //time consuming task
end
@(m_vif.drv_cb);
m_vif.drv_cb.One_Valid <= 0;
end
else if(item_h.m_trans_type == SECOND_TYPE) begin
automatic int delay_cycles = $urandom_range(m_cfg_h.min_dly,m_cfg_h.max_dly);
repeat (delay_cycles)
if(some_condition) begin
m_vif.drv_cb.Second_Valid <= 1;
m_vif.drv_cb.Second_Hdr <= item_h.m_hdr;
m_vif.drv_cb.Second_Data <= item_h.m_data;
end
else begin
automatic item_t auto_item_h = item_h;
check_two_credits_and_drive(auto_item_h); //time consuming task
end
@(m_vif.drv_cb);
m_vif.drv_cb.Second_Valid <= 0;
end
endtask :drive_item
Thanks for sharing your code. Unfortunately the interesting piece of code was missing. item_t.
Please provide it.
Another remarks:
(1) Using the process variables is not relevant in the UVM. You can remove it. This makes your code more easy.
(2) Do you have low-active reset. The namimg looks like this. In the code your reset is considered as highh active.
(3) You could randomize the delay _cycles outside of theh driver, by putting it to the seq_item item_t.
Another remarks:
(1) Using the process variables is not relevant in the UVM. You can remove it. This makes your code more easy.
-Sure.
(2) Do you have low-active reset. The namimg looks like this. In the code your reset is considered as high active.
-It’s active low and there is no issue in the reset logic I think.
(3) You could randomize the delay _cycles outside of the driver, by putting it to the seq_item item_t.
-But these delay parameters are from cfg class. So having cfg handle in every transaction didn’t seem to be good idea to me.
You can pass the constraints from the config object and use it here.
This randomizes your seq_item data memebers wrt your needs.
And I believ you can also simplify your driver code.
If your interface protocol allows the valid signal to be high during execution, then assert it at the beinning of the data transfer and de-assert it after the tarnsfer has been finished.
And please remove the mechanism with the process variables. It is not needed.