Timing Statements

Event Statements

Analog event statements do two distinct things. First, they force the analog kernel to place an evaluation point at a particular point in time. Second, they optionally execute a statement at that instant of time. For example, the following code implements a periodic sample and hold:

analog begin
   @(timer(0, T)) hold = V(in);
   V(out) <+ transition(hold, 0, 100n);
end

In this example, the simulator will stop at t=0, t=T, t=2T, etc. and at each of those points it will execute ‘hold = V(in)’. It will not execute this statement at any other points in time.

Timer Event

The @ statement is referred to as the analog event statement and timer() is an analog event function. The event function generates events at particular instants of time and forces the simulator to evaluate the circuit at those points. The event statement catches those events and evaluates the subsequent statement only at those events.

timer(time[, period][, ttol][, enable])
Parameters:
  • time (real) – time of needed evaluation point

  • period (real) – period or repetition

  • ttol (real) – time tolerance

  • enable (real) – enable

Return type:

event

If timer given with one argument it is the time of the next event. This case is used to model a noisy oscillator:

analog initial begin
   seed = 286;
   next = 0.5/freq;
end
analog begin
   @(timer(next)) begin
      hi = !hi;
      dT = jitter*$rdist_normal(seed, 0, 1);
      next = next + 0.5/freq * `M_SQRT1_2*dT;
   end
   V(out) <+ transition(hi ? Vhi : Vlo, 0, tt);
end

If timer is given with two arguments, the first is the time of the first event and the second its repetition period. So in the example above, the first event would occur at t=0 and then they would reoccur every T seconds after that.

Specifying ttol allows events that are close to be combined for efficiency reasons.

Cross Event

The cross() event function generates events at threshold crossings.

cross(wave[, dir][, ttol][, tol][, enable])
Parameters:
  • wave (real) – look for zero crossings in this waveform

  • dir (real) – direction (use +1 for rising, -1 for falling, 0 for either)

  • ttol (real) – time tolerance

  • tol (real) – waveform tolerance

  • enable (real) – enable

Return type:

event

The first argument to cross is an expression whose value varies with time. The cross function generates events when the value of the expression crosses 0. The second argument is either -1, 0, or +1. If it is -1, events are only generated on falling transitions (transitions from positive values to negative values). If it is +1, events are only generated on rising transitions (transitions from negative values to positive values). And if it is 0, events are generated on both rising and falling transitions.

The following model uses cross to count rising threshold crossings:

analog begin
   @(cross(V(in) - vth, +1))
      crossings = crossings + 1;
end

The simulator tries to stop as close to the actual crossing as it can, but due to numerical errors and simulation tolerances it might not be able to stop exactly at the crossing. However, it is guaranteed that the event will be placed at, or just slightly beyond, the crossing. In the following example, Last Crossing is used to remove this potential source of error from a measurement of the time of crossing:

analog begin
   t = last_crossing(V(in) - vth, +1);
   @(cross(V(in) - vth, +1)) begin
      if (t0 > 0)
         $strobe("period = %rs (measured at %rs).", t - t0, $abstime);
      t0 = t;
   end
end

The cross() event will only trigger in a transient analysis generates an event if the first argument transitions through 0 as a function of time. This cannot happen in DC or IC analyses because in these analyses signals are not changing with time. They compute equilibrium points, which by definition do not change with time. This can make use of the cross functions dangerous at times. Consider the analog model for a digital inverter:

// do not use this version
analog begin
   @(cross(V(in) - vth))
      lv = V(in) > vth;
   V(out) <+ transition(lv ? vh : vl, 0, tt);
end

Because the cross function does not trigger during a DC or IC analysis, the inverter will not know what the logic value of its input is at t=0. There are two ways to solve this problem. First, you can compute the logic value of the input outside the event statement, as follows:

analog begin
   @(cross(V(in) - vth))
      ;
   V(out) <+ transition(V(in) > vth ? vh : vl, 0, tt);
end

Notice in this case there was nothing to do at the event time, however we still needed the event statement (it is not possible to use an event function without an event statement). As such, we just associated an empty statement with the event statement. In this way, the cross function is present to force the simulator to place an evaluation point at the threshold crossing without having anything else to do at that point.

Above Event

Another way to address this problem is to use the above event function.

above(wave[, ttol][, tol][, enable])
Parameters:
  • wave (real) – look for zero crossings in this waveform

  • ttol (real) – time tolerance

  • tol (real) – waveform tolerance

  • enable (real) – enable

Return type:

event

above is similar to cross except it operates in both DC and IC analyses and it does not support a direction argument (effectively the direction argument is fixed to be +1). Thus this model can be rewritten to be:

analog begin
   @(above(V(in) - vth) or above(vth - V(in)))
      lv = V(in) > vth;
   V(out) <+ transition(lv ? vh : vl, 0, tt);
end

Notice that two or more event functions can be placed in the same event statement if you separate them with the keyword or.

Initial and Final Step Events

Two more analog event functions are available: initial_step and final_step. The initial_step function generates an event at the first evaluation point and so can be used for initialization. This gives us yet another way to write the inverter:

analog begin
   @(initial_step or cross(V(in) - vth))
      lv = V(in) > vth;
   V(out) <+ transition(lv ? vh : vl, 0, tt);
end

The final_step function generates an event at the last evaluation point and so can be used for clean up. In the following example, it used for post simulation computation:

analog begin
   t0 = last_crossing(V(arm) - thresh, dir);
   @(cross(V(arm) - thresh, dir))
      armed = 1;
   t1 = last_crossing(V(trigger) - thresh, dir);
   @(cross(V(trigger) - thresh, dir)) begin
      if (armed) begin
         armed = 0;
         count = count +1;
         sum = sum + (t1 - t0);
      end
   end
   @(final_step) begin
      $strobe("delay measurements = %d.\n", count);
      if (count) begin
         mean = sum / count;
         $strobe("delay mean (est)= %g.\n", mean);
      end else
         $strobe("Could not measure delay.\n");
   end
end

Note

Cadence’s AMS simulator does not allow the initial_step and final_step events to be used in event statements found in discrete processes (initial or always).

Discrete Events

In Verilog-AMS, analog event statements may also contain discrete events:

analog begin
   @(posedge ref) state = state + 1;
   @(posedge fg) state = state - 1;
   if (state > 1) state = 1;
   if (state < -1) state = -1;
   I(out) <+ Iout * transition(state, 0, 10n);
end

Note

Cadence’s AMS simulator only supports discrete transitions filtered through posedge or negedge. Putting a discrete wire, discrete variable, or named event directly in a continuous event statement is not supported (as of 2014). You can use the following as a work around:

real vgain;
always @(gain) vgain = pow(10, (gain - 32.0)/20);

real voffset;
always @(offset) voffset = 1m*offset;

reg break = 0;
always @(vgain or voffset or en) break <= break;

analog begin
   @(posedge break or negedge break)
      ;
   V(out) <+ transition(en ? vgain : 0, 0, 100n) * V(in) + voffset;
end