You have some issues here. In addr_phase(), you don’t know who tx is, since that’s a local variable in the bigger task (guessing it’s your run_phase(…)). You’ll also be getting items like crazy there, since you don’t block at all. It’s also more complicated than it needs to be.
I’d do something along these lines:
task run_phase(uvm_phase phase);
forever begin
xyz_seq_item tx;
seq_item_port.get_next_item(tx);
drive(tx);
seq_item_port.item_done();
end
endtask
// block until the address phase is done, at which point a new
// item could be started
task drive(xyz_seq_item item);
drive_addr_phase(item);
fork
drive_data_phase(item);
join_none
endtask
task drive_addr_phase(xyz_seq_item item);
// your logic here
endtask
task drive_data_phase(xyz_seq_item item);
// your logic here
endtask
This code is off the top of my head so it might not compile directly. It avoids having to use any thread synchronization here, so it should be simpler to follow.