Why doesn't it update?

Hi,

I am new to Systemverilog (but coming from C/C++) and definitely fiddling with the language here.
Could you tell me why the referenced variable is not updated in the module sub?

I thought that changing the ref value would send an event to the module.


module sub(ref int toto);
  int res = 0;
  int foo = 0;
  int bar = 0;
  int count = 0;

  initial   
    forever // plain forever to try full async event logic (no clock desired)
      begin
        unique case (toto)
          3: 
            begin
              if (foo == 0) 
                begin
                  foo++;
                  res = 5;
                  $display("Show %d %d",res,count);
            
                end
            end
          5: 
            begin
              if (bar == 0) 
                begin
                  bar++;
                  res = 89;
                  $display("--- Breaking from module. Show %d %d",res,count);
                  break;
                end
            end
          default: 
            count++;
        endcase
      end
endmodule

module main();

  int ki = 0;
  sub ju(ki);

  initial 

    begin
      #50 ki = 3; // first update
      #50 ki = 5; // should break the sub module loop
      $finish;
    end

endmodule

In reply to MarkLand:

What results are you seeing? What results do you expect to see?

Just by looking at your code, I’m going to guess that you are experiencing an infinite loop in your initial-forever block. SystemVerilog will execute the forever loop in 0 time until there is a blocking event which allows time to advance. Without this, you will never get past time 0.

In your sub-module, you want to have an always @(ki) so that your module will react to changes in ki.

In reply to cgales:

Thanks: actually, I was looking at this piece of code Tiempo CPU

And I was trying to check/simulate an event the same way they did.


module FSM(
    push_channel_opcode_t.out OP,
    push_channel_event_type.out ST,
    push_channel_go_t.in GO,
    push_channel_bit.in  AB);

  always
    begin: fsm_process  
      go_t go;
      bit ab;
      opcode_t op;
      static state_t state = S0;
      unique case(state)
        S0:
            GO.Read(go);
            unique case(go)
              SEQ1:
                state = S1; //why not non blocking here?
              SEQ2:
                state = S2;
            endcase
          
        S1,S2:
            /* do smthg */
        S3:
            /* do smthg */
      endcase
    end: fsm_process  
endmodule

And they said

An asynchronous process specifies a dataflow network relating a set of channels read by the process to another set of channels written by the process. The synchronization is exclusively done through channel handshake operations. Signal event statements (e.g., posedge, negedge) are not used. An asynchronous process is implemented by a SystemVerilog always process statement containing neither event control nor event expression. As they imply event control statements, the always_ff, always_latch and always_comb processes cannot be used to model asynchronous processes.

So I tried the bare forever (+break statement) since I cannot simulate a plain always (infinite loop). And nothing happened.

My expected result is:
“Show res 5 C” (C being a decimal count value)
followed by
“— Breaking from module. Show 89 C” (C being a decimal count value)

Is it a mistake in their code?
Why aren’t the assignments non blocking (state <= S1; instead of state = S1;), since the goal is to be fully decoupled?

I am not even sure that it is an efficient solution, because the infinite loop may increase current consumption.

I may be wrong. Anyone able to answer?

In reply to MarkLand:

The example you are looking at is atypical from the way most RTL is written today. Most state machines (FSMs) execute with a synchronous clock; advancing to the next state every clock cycle.

The code in the Tiempo CPU is asynchronous. It does not get into an infinite loop because there are calls to tasks like read and write that wait for other events.

I don’t think this is a good example for someone just learning SystemVerilog.

In reply to dave_59:

Thanks Dave,
I missed one thing: I can recreate such behavior with event triggering/wait.

That may be the missing link.

I am used to async programming but in C. System verilog is a different beast, but I will feel at home in no time.

Thanks!

And for the sake of completeness, a piece of working code.
I put the wait in each case statement because I wanted to simulate a function call.


module sub(ref int toto, input event my_ev);
  int ml = 0;
  int fou = 0;
  int kol = 0;
  int count = 0;

  always 
      begin
        case (toto)
          3: 
                begin
                  ml++;
                  fou = 54;
                  $display("hh %d %d",fou,count);
                  #5 wait(my_ev.triggered);
                  $display("out of 3 case");
                end
         
          5: 
        
           
                begin
                  kol++;
                  fou = 89;
                  $display("polo %d %d",fou,count);
                  #5 wait(my_ev.triggered);
                  $display("out of 5 case");
                 
                end
           
          default: 
            begin
              count++;
              $display("default case %d",toto);
              #5 wait(my_ev.triggered);
              $display("out of default case %d fin",toto,$time);
            end
        endcase
      end
endmodule

module main();

  int ki = 0;
  event my_ev;
  sub sub(ki,my_ev);


  initial 
    begin
      #5 ki = 4;
      ->my_ev;
      #5 ki = 3;
      ->my_ev;
      #5 ki = 5;
      ->my_ev;
      $display("FINISH");
    end

endmodule

In reply to MarkLand:

I don’t understand why you are trying to make things more complex than necessary. Instead of trying to simulate a function call, why don’t you use an actual function call? Modules are designed for time-based hardware constructs. Functions are for zero time processing, just like in C.



function void sub(int toto);
  automatic int ml = 0;
  automatic int fou = 0;
  automatic int kol = 0;
  automatic int count = 0;

      begin
        case (toto)
          3: 
                begin
                  ml++;
                  fou = 54;
                  $display("hh %d %d",fou,count);
                  $display("out of 3 case");
                end
          5: 
                begin
                  kol++;
                  fou = 89;
                  $display("polo %d %d",fou,count);
                  $display("out of 5 case");
                end
          default: 
            begin
              count++;
              $display("default case %d",toto);
              $display("out of default case %d fin",toto,$time);
            end
        endcase
      end
endfunction
 
module main();
 
  int ki = 0;

  initial 
    begin
      #5 ki = 4;
      sub(ki);
      #5 ki = 3;
      sub(ki);
      #5 ki = 5;
      sub(ki);
      $display("FINISH");
    end
 
endmodule

In reply to cgales:

My bad, I expressed myself incorrectly.

Actually, I meant the “wait” in the sub module were simulating a function call. The wait could be replaced by a synchronous read/write event of some sort to get away from a global clock and try to be as asynchronous as possible. But to keep it contrived in this example, this was the option I chose.
The module will run in parallel all by itself doing stuff by itself.

The sub module will consume time (event based).

But maybe there is a detail I missed in my understanding of SystemVerilog.