Pass by value
In SystemVerilog, Pass by value is the default mechanism for passing arguments to subroutines. This argument passing mechanism works by copying each argument into the subroutine area. If the subroutine is automatic, then the subroutine retains a local copy of the arguments in its stack.
Pass by reference
Arguments passed by reference are not copied into the subroutine area, rather, a reference to the original argument is passed to the subroutine.
The subroutine can then access the argument data via the reference. No casting shall be permitted.
Also note, It shall be illegal to use argument passing by reference for subroutines with a lifetime of static.
Only the following shall be legal to pass by reference:
— A variable,
— A class property,
— A member of an unpacked structure, or
— An element of an unpacked array.
Nets and selects into nets shall not be passed by reference
For your question
What is the purpose "ref yyy" in the modport ?
There are some performance improvement when you use reference to an event when different events gets triggered multiple times, but it is a good practice to use only when considerable performance optimizations are required/necessary. One recommended example to use pass by reference can be found in the link
Here I've shown one way of how modport with ref event is used.
interface intf(input clk);
event out_event;
logic clk;
modport dut(input clk,ref out_event);
always @(out_event)
$display ($time,"ns"," out_event triggered");
endinterface
module dut( intf.dut d);
reg [2:0] count;
initial
count = 0;
always @ (posedge d.clk)begin
count = count + 1'b1;
if ( count == 'd2)
-> d.out_event;
end
endmodule
module top (input clk);
intf int_f(.clk(clk));
dut u0(.d(int_f.dut));
endmodule
The above working example can be found in the link EDA-Playground
For more details about Pass by value refer Section 13.5.1 of SystemVerilog LRM IEEE 1800-2012 and for Pass by reference refer Section 13.5.2