[dv, clk_rst_if] Improve jitter and add scaling

- Cleanup existing code / comments
- Replace `ifdef VERILATOR` with existing macros
- Add support for randomly scaling the frequency on each edge with +-
%tage over the initially set frequency
- Improve jitter generation - use `$urandom` instead of
`std::randomize`.
- Fix jitter generation bug where the originally set clk hi and lo half
periods get modified

Signed-off-by: Srikrishna Iyer <sriyer@google.com>
diff --git a/hw/dv/sv/common_ifs/clk_rst_if.sv b/hw/dv/sv/common_ifs/clk_rst_if.sv
index 60fe637..f984c0a 100644
--- a/hw/dv/sv/common_ifs/clk_rst_if.sv
+++ b/hw/dv/sv/common_ifs/clk_rst_if.sv
@@ -26,23 +26,70 @@
   import uvm_pkg::*;
 `endif
 
-  bit drive_clk;              // enable clk generation
-  logic o_clk;                // output clk
+  // Enables clock to be generated and driven by this interface.
+  bit drive_clk;
 
-  bit drive_rst_n;            // enable rst_n generation
-  logic o_rst_n;              // output rst_n
+  // The internal output clock value.
+  logic o_clk;
 
-  // clk params
-  bit clk_gate      = 1'b0;   // clk gate signal
-  int clk_period_ps = 20_000; // 50MHz default
-  real clk_freq_mhz = 50;     // 50MHz default
-  int duty_cycle    = 50;     // 50% default
-  int max_jitter_ps = 1000;   // 1ns default
-  bit recompute     = 1'b1;   // compute half periods when period/freq/duty are changed
-  int clk_hi_ps;              // half period hi in ps
-  int clk_lo_ps;              // half period lo in ps
-  int jitter_chance_pc = 0;   // jitter chance in percentage on clock edge - disabled by default
-  bit sole_clock = 1'b0;      // if true, this is the only clock in the system
+  // Enables the rst_n to be generated and driven by this interface.
+  bit drive_rst_n;
+
+  // The internal output reset value.
+  logic o_rst_n;
+
+  // Applies clock gating.
+  bit clk_gate = 1'b0;
+
+  // The nominal (chosen) frequency (as period in ps) of the driven clock (50MHz by default).
+  int clk_period_ps = 20_000;
+
+  // The variation of clock period (mimics uncalibrated clocks), to scale the nominal frequency
+  // down. If set to non-zero value, the clock frequency is scaled randomly on every edge.
+  int clk_freq_scaling_pc = 0;
+
+  // The percentage chance of freq scaled down randomly on each edge.
+  int clk_freq_scaling_chance_pc = 50;
+
+  // Clock frequency is scaled down. This enables the frequency to be randomly scaled up as well.
+  //
+  // Note: If set, the randomness of the clock frequency being scaled up or down may result in a
+  // bigger frequency distribution than the intended clk_freq_scaling_pc setting. For example, 50MHz
+  // with 10% scaling may result in pulses that are < 45MHz and > 55MHz wide as well.
+  bit clk_freq_scale_up = 1'b0;
+
+  // The computed clock frequency in MHz.
+  real clk_freq_mhz = 50;
+
+  // The duty cycle of the clock period as percentage. If jitter and scaling is applied, then the
+  // duty cycle will not be maintained.
+  int duty_cycle = 50;
+
+  // Maximum jitter applied to each period of the clock - this is expected to be about 20% or less
+  // than the clock period. The computed jitter is added or subtracted to each edge.
+  //         _________
+  // _____:_| :     : |_:_______
+  //
+  // The actual jitter value is picked randomly within the window {[-max_jitter_ps:max_jitter_ps]}
+  // and is added to the time to next edge.
+  int max_jitter_ps = 1000;
+
+  // The percentage chance of jitter occurring on each edge. If 0 (default value), then jitter is
+  // disabled altogether. If 100, jitter is computed and applied at every edge.
+  int jitter_chance_pc = 0;
+
+  // Internal signal indicating the clock half periods  need to be recomputed.
+  bit recompute = 1'b1;
+
+  // Internal signal indicating the amount of time for which the clock stays high / lo in the next
+  // cycle.
+  int clk_hi_ps;
+  int clk_lo_ps;
+  real clk_hi_modified_ps;
+  real clk_lo_modified_ps;
+
+  // If true, this is the only clock in the system; there is no need to add initial jitter.
+  bit sole_clock = 1'b0;
 
   // use IfName as a part of msgs to indicate which clk_rst_vif instance
   string msg_id = {"clk_rst_if::", IfName};
@@ -81,6 +128,22 @@
     set_freq_khz(freq_mhz * 1000);
   endfunction
 
+  // Set the clk frequency scaling, chance in percentage and scaling up.
+  //
+  // freq_scaling_pc is a positive integer that determines by what amount (as percentage of the
+  // nominal frequency) is the frequency scaled (jittered) down.
+  // freq_scaling_chance_pc is a percentage number between 0 and 100 that determines how often is
+  // the scaling randomly recomputed and applied.
+  // freq_scale_up is a bit that enables the random scaling up of the frequency as well.
+  function automatic void set_freq_scaling(int freq_scaling_pc, int freq_scaling_chance_pc = 50,
+                                           bit freq_scale_up = 1'b0);
+    `DV_CHECK_FATAL(freq_scaling_pc >= 0, , msg_id)
+    `DV_CHECK_FATAL(freq_scaling_chance_pc inside {[0:100]}, , msg_id)
+    clk_freq_scaling_pc = freq_scaling_pc;
+    clk_freq_scaling_chance_pc = freq_scaling_chance_pc;
+    clk_freq_scale_up = freq_scale_up;
+  endfunction
+
   // call this function at t=0 (from tb top) to enable clk and rst_n to be driven
   function automatic void set_active(bit drive_clk_val = 1'b1, bit drive_rst_n_val = 1'b1);
     time t = $time;
@@ -89,11 +152,7 @@
       drive_rst_n = drive_rst_n_val;
     end
     else begin
-`ifdef VERILATOR
-      $error({msg_id, "this function can only be called at t=0"});
-`else
-      `uvm_fatal(msg_id, "this function can only be called at t=0")
-`endif
+      `dv_fatal("This function can only be called at t=0", msg_id)
     end
   endfunction
 
@@ -106,13 +165,7 @@
 
   // set the duty cycle (1-99)
   function automatic void set_duty_cycle(int duty);
-    if (!(duty inside {[1:99]})) begin
-`ifdef VERILATOR
-      $error({msg_id, $sformatf("duty cycle %0d is not inside [1:99]", duty)});
-`else
-      `uvm_fatal(msg_id, $sformatf("duty cycle %0d is not inside [1:99]", duty))
-`endif
-    end
+    `DV_CHECK_FATAL(duty inside {[1:99]}, , msg_id)
     duty_cycle = duty;
     recompute = 1'b1;
   endfunction
@@ -125,13 +178,7 @@
   // set jitter chance in percentage (0 - 100)
   // 0 - dont add any jitter; 100 - add jitter on every clock edge
   function automatic void set_jitter_chance_pc(int jitter_chance);
-    if (!(jitter_chance inside {[0:100]})) begin
-`ifdef VERILATOR
-      $error({msg_id, $sformatf("jitter_chance %0d is not inside [0:100]", jitter_chance)});
-`else
-      `uvm_fatal(msg_id, $sformatf("jitter_chance %0d is not inside [0:100]", jitter_chance))
-`endif
-    end
+    `DV_CHECK_FATAL(jitter_chance inside {[0:100]}, , msg_id)
     jitter_chance_pc = jitter_chance;
   endfunction
 
@@ -153,22 +200,30 @@
     clk_gate = 1'b1;
   endfunction
 
-  // add jitter to clk_hi and clk_lo half periods based on jitter_chance_pc
-  function automatic void add_jitter();
-    int jitter_ps;
+  // Scales the clock frequency up and down on every edge.
+  function automatic void apply_freq_scaling();
+    real scaling;
+    real mult = $urandom_range(0, clk_freq_scale_up) ? 1.0 : -1.0;
+
+    if ($urandom_range(1, 100) <= clk_freq_scaling_chance_pc) begin
+      scaling = 1.0 + mult * real'($urandom_range(0, clk_freq_scaling_pc)) / 100;
+      clk_hi_modified_ps = clk_hi_ps * scaling;
+      scaling = 1.0 + mult * real'($urandom_range(0, clk_freq_scaling_pc)) / 100;
+      clk_lo_modified_ps = clk_lo_ps * scaling;
+    end
+  endfunction
+
+  // Applies jitter to clk_hi and clk_lo half periods based on jitter_chance_pc.
+  function automatic void apply_jitter();
+    int jitter;
+
     if ($urandom_range(1, 100) <= jitter_chance_pc) begin
-`ifndef VERILATOR
-      `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(jitter_ps,
-          jitter_ps inside {[-1*max_jitter_ps:max_jitter_ps]};, "", msg_id)
-`endif
-      clk_hi_ps += jitter_ps;
+      jitter = ($urandom_range(0, 1) ? 1 : -1) * $urandom_range(0, max_jitter_ps);
+      clk_hi_modified_ps = clk_hi_ps + jitter;
     end
     if ($urandom_range(1, 100) <= jitter_chance_pc) begin
-`ifndef VERILATOR
-      `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(jitter_ps,
-          jitter_ps inside {[-1*max_jitter_ps:max_jitter_ps]};, "", msg_id)
-`endif
-      clk_lo_ps += jitter_ps;
+      jitter = ($urandom_range(0, 1) ? 1 : -1) * $urandom_range(0, max_jitter_ps);
+      clk_lo_modified_ps = clk_lo_ps + jitter;
     end
   endfunction
 
@@ -215,11 +270,7 @@
         o_rst_n <= 1'b1;
       end
       default: begin
-`ifdef VERILATOR
-        $error({msg_id, $sformatf("rst_n_scheme %0d not supported", rst_n_scheme)});
-`else
-        `uvm_fatal(msg_id, $sformatf("rst_n_scheme %0d not supported", rst_n_scheme))
-`endif
+        `dv_fatal($sformatf("rst_n_scheme %0d not supported", rst_n_scheme), msg_id)
       end
     endcase
     wait_clks(post_reset_dly_clks);
@@ -252,13 +303,15 @@
       if (recompute) begin
         clk_hi_ps = clk_period_ps * duty_cycle / 100;
         clk_lo_ps = clk_period_ps - clk_hi_ps;
+        clk_hi_modified_ps = clk_hi_ps;
+        clk_lo_modified_ps = clk_lo_ps;
         recompute = 1'b0;
       end
-      if (jitter_chance_pc != 0) add_jitter();
-      #(clk_lo_ps * 1ps);
-      // wiggle output clk if not gated
+      if (clk_freq_scaling_pc && clk_freq_scaling_chance_pc) apply_freq_scaling();
+      if (jitter_chance_pc) apply_jitter();
+      #(clk_lo_modified_ps * 1ps);
       if (!clk_gate) o_clk = 1'b1;
-      #(clk_hi_ps * 1ps);
+      #(clk_hi_modified_ps * 1ps);
       o_clk = 1'b0;
     end
   end