Watchdog Timer

Hello,

Can anybody tell me how to implement a watchdog timer. I would like to end the simulation if for a defined number of cycles no activity happens on the output interface. Something that I would like to use to detect hangs in RTL etc.

Any code snippet would be useful.

Thanks in advance,
Madhu

In reply to mseyunni:

could be as simple as a down counter.

module counter_max #(MAX_COUNT=99999)( // parameter port list 
            output logic timeout, 
            input logic clk, rst_n,
            input bit activity) ;
    	int counter=MAX_COUNT;
 
        always @(posedge clk) begin : counter1
            if(!rst_n || activity) counter <= MAX_COUNT; 
            else counter <= counter - 1'b1;
            if(counter==0) timeout <= 1'b1; 
            else timeout <= 1'b0;
        end : counter1
	
    endmodule : counter_max

In reply to ben@SystemVerilog.us:

Thanks Ben. But, I would like to implement similar stuff in UVM to stop the simulation / test. How can I use this in the UVM world ?

Madhu

In reply to mseyunni:

Since no one replied, can you consider something like this in your test?
(Using an old uvm model I have as an example)

class rand_test_sq extends counter_base_test;
// ....
    virtual task run_phase(uvm_phase phase); // run();
        rand_seq seq;
        int counter=MAX_COUNT;
        seq = rand_seq::type_id::create("seq");
        uvm_test_done.raise_objection(this);
        `uvm_info({get_type_name(),":run"}, "Starting test...", UVM_LOW)
        #1;
        fork: fork_run
            //Connect sequence to sequencer and start
            begin 
                $display("Start m_test_sequencer %t", $time);
                seq.start(m_test_sequencer, null);
                // anoher seqx start 
            end 
            // #50000;
            begin 
              forever begin 
                @env_0.agent0.driver.vif.driver_cb begin : counter1
                 if(path_to.activity) counter <= MAX_COUNT; 
                    else counter <= counter - 1'b1;
                 if(counter==0) 
                     uvm_test_done.drop_objection(this); 
               end : counter1
              end             
            end 
        join_any: fork_run
        `uvm_info({get_type_name(),":run"}, "... test completed", UVM_LOW)
        uvm_test_done.drop_objection(this);
    endtask: run_phase

In reply to ben@SystemVerilog.us:

Hi Ben,

Thank you for the code snippet. I have a question regarding the above code. In the first thread of fork-join_any lets say we fork off request threads. After all the requests have been sent to DUT fork-join_any exists and so the objection is dropped. I’m not sure whether the above code waits for reception of all the responses. I have a requirement where I would like to end the simulation whenever I have seen all the responses out of DUT or the timeout whichever occurs earlier. Also the request and response interfaces being different, how can we update / modify the above code to suit my requirements.

Thanks,
Madhu

In reply to mseyunni:

  1. Instead of the “forever” you can use something like
    "while(counter !=0) begin " // with counter initialized to something > 0
    That would force the fork to join if the counter==0
    I asked a friend who is more knowledgeable on UVM to provide a response. I hope he does.
    Ben

In reply to ben@SystemVerilog.us:

Thanks a lot Ben. I am grateful for the care & attention to provide me response that I requested.

Regards,
Madhu

In reply to ben@SystemVerilog.us:

Hi Ben,

Thanks for the response. I am not very clear on what you meant by:

(the checker version would not add either if you use the counter there)

Is the module_dut is only for a demonstration purposes ? or is it part of my solution to detect a condition to stop the simulation ?

Also, I am not very clear on the use of assertions here ?

I thought the previous solution of starting a counter and resetting the counter upon seeing an activity was something like what I was looking for, which is to look for idle cycles of some duration (to detect cases such as responses lost in the DUT) to enable me to stop the simulation. The other criteria to stop the simulation as I said in my post earlier is to see whether I received all the responses out (the request and response interfaces being different but luckily on the same clock). However, I would be interested to know if the request and response were different clocks.

Finally, my original requirement to was to determine the end of simulation criteria and to stop the simulation both in case of graceful end and simulation hang (timeout) cases. I see this more of a verification problem.

Thanks,
Madhu

In reply to mseyunni:

I talked with my co-authors about some suggestions for your requirements, which are:

I have a requirement where I would like to end the simulation whenever I have seen all the responses out of DUT or the timeout whichever occurs earlier. Also the request and response interfaces being different, how can we update / modify the above code to suit my requirements.

They suggested something that I should have done initially, is to use SVA with uvm_error or uvm_fatal.
The assertions can be put in the DUT, or in interfaces, or in checkers bound to DUT or interfaces, or in the testbench module.
The use of assertions is much better than a plain counter as it would not add to synthesis area (the checker version would not add either if you use the counter there)
Below is an example for the application of assertions with uvm_error and uvm_fatal.

import uvm_pkg::*; `include "uvm_macros.svh"
module dut #(parameter MAX_DEL = 200) 
 (input logic clk, rst_n, output logic activity);

  logic [7:0] count;

  always_ff @(posedge clk) begin : act
    if (!rst_n) begin : reset
      count <= 0;
      activity <= 1'b0;
    end : reset
    else begin : work
      if (count < MAX_DEL) begin : b1
        activity <= 0;
        count++;
      end : b1 
      else begin : b2
        count <= 0;
        activity <= 1;
      end : b2
    end : work
  end : act

endmodule : dut

module tb;
  parameter MAX_IDLE = 30;
 bit clk, rst_n, activity;

  dut dut (.*);

  always #10 clk <= ~clk;

  default clocking @(posedge clk);
  endclocking

  initial begin : test
    ##10;
    rst_n <= 1'b1;
    ##10000 $finish (2);    
  end : test

  a_wake_up_after_rst : assert property (p_wake_up) 
             `uvm_error("ID", "did not wake up"; // removed the "else" 
  a_dont_be_lazy : assert property (p_dont_be_lazy) 
             `uvm_fatal("ID", "did not wake up_other assertion");

  property p_wake_up;
    $rose (rst_n) |-> ##[1:MAX_IDLE] !$stable(activity);
  endproperty : p_wake_up

  property p_dont_be_lazy;
    !$stable (activity) |-> ##[1:MAX_IDLE] !$stable(activity);
  endproperty : p_dont_be_lazy

endmodule : tb

In reply to mseyunni:

I am not very clear on what you meant by:

A checker is ignored by synthesis tools, thus, any code there (like with “always_ff”) or any other code is not considered as part of the RTL design. More on checker below from my SVA book.

The origin of the checker construct stems from the formal verification world, and hence, certain restrictions are imposed in the modeling styles of a checker. A checker is not intended to be a replacement to a module/interface , as its goal is strictly for verification. Hence the modeling styles allowed in a checker represent a subset suitable for the definition of assertions and coverage with the supporting environment. IEEE 1800-2012 significantly enhanced the 1800-2009 version of a checker to encapsulate a set of properties, assertions, cover statements, assumptions, and any auxiliary procedural code needed to validate the behavior of the design in simulation and in formal verification. It is expected that the checker will play a significant role in the adoption of assertions and in the definition of assertion libraries. One significant advantage of checkers is that they clearly mark the scope of any auxiliary code used to support assertion and verification libraries. This clear demarcation between assertion library code and RTL code enhances code understanding; eliminates potential conflicts between RTL code and supporting code for assertions (such as the use of always blocks and updates of variables needed for assertions); and allows synthesis or other processing tools to concentrate on one specific region of the model, such as the RTL only.

Also, I am not very clear on the use of assertions here ?

The RTL that I showed was intended to represent your RTL. Under normal simulation where there is always activity (i.e., no lock state, and the uvm_error would not be triggered) your simulation would end under your UVM control normal ending. Under a locked state, the assertions will fail, thus causing the action block (the uvm_error) to fire, thus ending the sim.

I thought the previous solution of starting a counter and resetting the counter upon seeing an activity was something like what I was looking for

Apologies, that assertion is indeed confusing and incorrect and not what you want; a misunderstanding. This is more of what you want.

import uvm_pkg::*; `include "uvm_macros.svh"
module dut #(parameter MAX_DEL = 200) 
 (input logic clk, rst_n, input logic activity, output int count);

  always_ff @(posedge clk) begin : act
    if (!rst_n)  count <= 0;
    else if (activity)  count <= 0;
    else begin 
    	count++;
    	if(count>=MAX_DEL) `uvm_error("ID", "did not wake up")
    end 
  end : act
  
   // The assertion below replaces the always_ff block
   ap_activity:  assert property(@(posedge clk) activity |-> ##[1:MAX_DEL] activity) 
     else  `uvm_error("ID", "did not wake up"); 
endmodule : dut

Thus, instead of count code, you can use assertions. With the always_ff, the synthesis tool may or may not include in your code. Instead, the assertion does not use the count, and assertions are ignored by synthesis. You could also make up an always construct that emulates this assertion, and the synthesis tool may or may not ignore it. Th assertion states that if there an an activity, there should be another one within 1 to MAX_DEL cycles.
If you have multiple conditions in different places that can trigger this “activity” signal, you could write separate assertions for the different situations, or you can combine them in a special interface.
I hope that this helps.

In reply to ben@SystemVerilog.us:

Hi Ben,

Thanks for persisting with my questions (hopefully this will be the last one). Though I understand the above code, apologies, I actually wanted this monitoring to be done somewhere (interface, a system monitor or in the test itself ) and use this as a way to stop the test/simulation in case there is a hang in the RTL. I see the above code could be used as the checker, but feeding this to test (which is what I was after) helps ending the test as well report it as a fatal / error with a message “Test ending due to timeout” etc.

Thanks,
Madhu

In reply to mseyunni:

Question for you: "How do you stop your sim now? If you use a
“uvm_test_done.drop_objection(this);”
then, add something like what I originally proposed, or put an assertion in one (or more interfaces to trigger a signal that can be used to do the drop_objection.
I am thinking of something like this

// in the SV interface 
  bit no_activity;
  function void set_no_activity(); 
    no_activity = 1'b1; 
    `uvm_error("ID", "did not wake up")
  endfunction 
  ap_activity:  assert property(@(posedge clk) activity |-> ##[1:MAX_DEL] activity) 
     else   set_no_activity(); 
endinterface

BTW, do you use any of these functions?
a) $finish
b) $stop
c) $exit
If so, they can be called from an assertion action block.
Can you show a snippet on how you currently end the sim?

In reply to ben@SystemVerilog.us:

"How do you stop your sim now? If you use a
“uvm_test_done.drop_objection(this);”

BTW, do you use any of these functions?
a) $finish
b) $stop
c) $exit
If so, they can be called from an assertion action block.

No. I am not using any of these function. As far as I know, uvm run_test() implicitly calls the $finish.

Yes. I am using something like in the main_phase of my test:

phase.raise_objection()
start the vseq;
phase.drop_objection()

I guess I should add logic of counting in the interface and use it as you have shown in your original example in a fork-join_any to end the simulation before dropping the objection.