My 'forever' inside a 'fork' doesn't act as I expect

So I communicate with the testbench over a socket in DPI-C, waiting on and receiving data is a blocking call. When the socket returns data, C then calls a setter function in SV, and returns from DPI-C thus unblocking SV processing.
I want to add an option to keep time “ticking”, so even if the DPI-C call is blocking, simulation time still advances (today the blocking call in DPI-C prevents simulation time from advancing).

With the following partial-code (I believe it should be sufficient) if I send the string “ticker” the command is processed, and I see printed out:

ticker
starting ticker
SystemVerilog loop over. Will now restart waiting for command
forking!
started ticker
0 - Calculating Delay from VALUE (1) and UNIT (ps)

But I expect to see that last line and Auto-Delay ticked several times, advancing time automatically and printing out the timestamp each time.
What am I doing wrong or misunderstanding?


int ticker_started = 0;

task perform_delay;
    input int delay_value;
    input string delay_unit;
    realtime delay_scale_unit;
    realtime final_delay;
    $display("%t - Calculating Delay from VALUE (%0d) and UNIT (%s)", $time, delay_value, delay_unit);
    case(delay_unit)
        "us" : begin
                 delay_scale_unit = 1us;
               end
        "ns" : begin
                 delay_scale_unit = 1ns;
               end
        "ps" : begin
                 delay_scale_unit = 1ps;
               end
        "fs" : begin
                 delay_scale_unit = 1fs;
               end
    endcase
    final_delay = delay_value * delay_scale_unit;
    #(final_delay);
    $display("END DELAY COMMAND");
endtask

task automatic my_ticker;
    input int delay_value;
    input string delay_unit;
    begin
        $display("started ticker");
        while (1) begin
            perform_delay(delay_value, delay_unit);
            //#(delay_value)
            $display("@%g - Auto-Delay ticked %d", $time, delay_value);
        end
    end
endtask


// ... inside the body of a test-sequence

    while (1) begin
        if (ticker_started) begin
            $display("forking!");
            fork
                my_ticker(1, "ps");
                get_commands__blocking(); // DPI-C call, which will populate the "instr" var
            join_any
            // the ticker should never end, but the get_commands will return once we get a new command
            $display("after join_any");
            // terminate all the threads
            disable fork;
        end else
            get_commands__blocking(); // DPI-C call, which will populate the "instr" var
        if (instr.substr(0,3) == "quit") begin
            $display("stopping RTL simulation");
            $finish;
        end
        else if (instr.substr(0, 5) == "ticker")) begin
            $display("ticker");
            if (ticker_started==0) begin
                $display("starting ticker");
                ticker_started = 1;
            end else begin
                $display("ending ticker");
                ticker_started = 0;
            end
        end
        $display("SystemVerilog loop over. Will now restart waiting for command");
     end
        

edit: note, I previously was using forever begin but changed it (inside my_ticker) to a while(1) just to check if it had different behavior.

In reply to ntmccork:

Simulation time cannot advance while blocked in a DPI-C function. Remember in Verilog, functions must return in 0 time.

What you might want to do is import get_command_blocking() as a DPi-C task, and have it call an exported SystemVerilog task to advance time. But realize that simulation time is very different from CPU time. You may want to check my DVCon paper: Easy Steps Towards Virtual Prototyping using the SystemVerilog DPI