Async clock edge event

// Code your testbench here
// or browse Examples
module top;
  bit clk_1 = 1;
  bit clk_2 = 0;
  
  always #63 clk_1 = ~clk_1;
  always #21 clk_2 = ~clk_2;
  
  initial begin
    @(negedge clk_1);
    @(posedge clk_2);
    $display("%0t: posedge clk_2", $time);
    #1000;
    $finish;
  end
  
  initial begin
    $dumpvars(0, top);
    $dumpfile("dump.vcd");
  end
endmodule

So at the first negative edge of clk_1, the positive edge of clk_2 also appears.
I tried most simulator. all of them execute $display(“%0t: posedge clk_2”, $time); at 105ns, which is the next posedge of clk_2 after the first negative edge of clk_1. Instead of 63ns.
This behaviour is desired because it is consistent with the behaviour of the two stage synchronizer in my rtl.

But I am worrying that if this output is deterministic or not.
Whether in some cases $display will execute at 63ns?

It is not deterministic. Generally events get processed in the order scheduled, but there is nothing preventing @(negedge clk_1) from resuming before the blocking assignment to clk_2. Certain optimizations might behave that way. If you want deterministic ordering, use a nonblocking assigment to clk_2.

I tried two way:

// Code your testbench here
// or browse Examples
module top;
  bit clk_1 = 1;
  bit clk_2 = 0;
  
  always #63 clk_1 <= ~clk_1;
  always #21 clk_2 <= ~clk_2;
  
  initial begin
    @(negedge clk_1);
    $display("%0t: negedge clk_1", $time);
    @(posedge clk_2);
    $display("%0t: posedge clk_2", $time);
    #1000;
    $finish;
  end
  
  initial begin
    $dumpvars(0, top);
    $dumpfile("dump.vcd");
  end
endmodule
// Code your testbench here
// or browse Examples
module top;
  bit clk_1 = 1;
  bit clk_2 = 0;
  
  always #63 clk_1 = ~clk_1;
  always #21 clk_2 <= ~clk_2;
  
  initial begin
    @(negedge clk_1);
    $display("%0t: negedge clk_1", $time);
    @(posedge clk_2);
    $display("%0t: posedge clk_2", $time);
    #1000;
    $finish;
  end
  
  initial begin
    $dumpvars(0, top);
    $dumpfile("dump.vcd");
  end
endmodule

The second version use non-blocking to clk_2 only. It will executes $display(“%0t: posedge clk_2”, $time); at 63ns. which is not I want
This prove that all non-blocking assignment’s LHS variables change after all blocking assignments end at a time slot?