Product Documentation
Real Number Modeling Guide
Product Version 22.09, September 2022

Modeling Examples

Examples of analog models and how these analog models have been converted to pure wreal models to enable mixed-signal connections.

Voltage Controlled Oscillator

Consider the example of a voltage-controlled oscillator (VCO). VCO is a standard analog block that is used for examples in PLLs. It creates an output oscillation with a given frequency. This frequency can be modified to some range by the input voltage of the block.

The following testbench specifies that a real variable (r_in) is changed every 10ns from one value to another. The real variable is assigned to the wreal wire (w_in). This wreal signal is connected to the input port of the VCO block that is instantiated in the testbench.

`include "disciplines.vams"
`timescale 1ns / 1ps

module top();
  wreal w_in;
  real r_in;
  vco vco (w_in, clk);

  always begin
    r_in = 1.0;
    #10 r_in = 1.2;
    #10 r_in = 0.2;
    #10 r_in = -0.2;
    #10 r_in = 1.345;
    #10 $finish;
  end
  assign w_in = r_in;
endmodule

The VCO block takes the wreal input signal and calculates at each change of the signal (@(vin)) the required output frequency. Remember that the wreal signal is event based. That means that there are discrete events whenever the signal changes and the signal stay constant otherwise.

The calculated output frequency is used to determine the delay time between the clock inversion operations. Please note that the frequency settings depend on the timescale used. In this case, we used a 1ns timescale resulting in a 1 GHz frequency unit.

module vco(vin, clk);
  input vin;
  wreal vin;
  output clk;
  reg      clk;
  real freq, clk_delay;
  real center_freq = 1; // freq in GHz
  real vco_gain = 1; // freq gain in GHz

  initial clk = 0;

  always @(vin) begin
    freq = center_freq + vco_gain*vin;
    clk_delay = 1.0/(2*freq);
  end
  always #(clk_delay) clk = !clk;
endmodule

The following figure shows the simulation result for this example.

This is a simple example where there are no parameters with value range restrictions for the constant definitions, input values of type `wrealXState and `wrealZState are not considered. It is possible to create negative frequency values and thus negative delay times, the clock frequency changes instantaneously with the input voltage change. All these – and probably more – issues and effects need to be considered for a good, robust, and reusable behavioral model. We will address some of these in later examples.

Low Pass Filter

The slewing effect is typical time-domain behavior of analog circuits. Other circuits are more dominated by their frequency-domain behavior, similar to filters. Continuous filters, for example, RC, or analog behavioral filters using the idt/ddt functions can be implemented directly in the analog domain. However, an equivalent behavior in the discrete domain is not available. A translation for the continuous filter function into a discrete z domain filter is required. This process is called the bilinear transform. References and theoretical details can be found in various textbooks and white papers.

First, let us see the testbench and the analog filter used. Study the code example below.

//xrun lpf.vams -input probe.tcl -access r analog.scs -gui
`include "constants.vams"
`include "disciplines.vams"
`timescale 1ns/1ps

module top ();
  electrical Vin, Vout;
  filter i_ctf (Vin, Vout);
  analog begin
    V(Vin) <+ sin($abstime * `M_TWO_PI * 1E8);
  end
endmodule

module filter (Vin, Vout);
  input Vin;
  output Vout;
  electrical Vout;
  electrical Vin;
  real r_in;
  real r_in_wreal;
  wreal r_out;

  parameter real Fp = 1e7;
  parameter real Dp = 0.5;
  real Wp;

  initial begin
    Wp = `M_TWO_PI*Fp;
  end
  // electrical implementation
  analog begin
    r_in = V(Vin);
    if (abs(r_in)<100n) r_in=0;
 // ignore small oscillations
       // second order low pass filter
    V(Vout) <+ idt(Wp*(
       idt(Wp*(r_in-V(Vout)),2*Dp*(r_in),0,1e-6 ) -
       2*Dp*V(Vout)),
       r_in,0,1e-6);
  end
  // wreal implementation
  always begin
    r_in_wreal = V(Vin);
    #1;
  end
  lpf #(.Fp(Fp), .Dp(Dp), .hfs(1G)) i_lpf_1 ( r_in_wreal, r_out );
endmodule // filter

A sine wave is transferred into the filter module. The core filter is a second-order low pass filter implemented by two integration functions. The corner frequency (Wp) and the damping factor (Dp) are used to parameterize the filter behavior.

The input signal is converted into a wreal signal at a fixed sampling rate. In this example, we know that the input is a sine input signal with a well-defined maximum frequency bandwidth, thus, we do not need to use the complex fix rate sampler block described above. However, for other input signals, a more complex sampling routine might be required to avoid aliasing effects. The input sampling rate for the low pass filter implementation is assumed to be a fixed rate. The rate is provided to the filter as hfs input parameter. In addition, the sampling rate is a critical factor for real live input signals. The sampling rate limits the frequency bandwidth of the sampled data by the Nyquist criteria, thus, the sampling rate of at least twice the maximum frequency of the input signal is required. However, the simulation performance obviously decreases with higher sampling rates. The right sampling rate is a trade-off that needs to be made based on the actual characteristics of the input signal and accuracy requirements.

The bilinear transform takes a two-step approach. It converts the analog differential equation into the s-domain and converts the s-domain data into the discrete z-domain in the second step.

The following is the basic differential equation of the filter function described above:

Vout = idt(Wp*(idt(Wp*(Vin-Vout))-2*Dp*Vout))

For the transformation into the frequency domain, all differentiations are replaced by multiplication by s while all integrations result in division by s. Note that we are neglecting most of the mathematical terminologies and variables naming conventions here to highlight the general process. Refer to standard literature for details.

The above example leads to the following equation:

s^2*Vout = (Wp*((Wp*(Vin-Vout))-2*Dp*s*Vout))
s^2*Vout + Wp^2*Vout + Wp*2*Dp*s*Vout = Wp^2*Vin

The linear transfer function H(s) is calculated as the s-domain output divided by the input:

H(s) = Vout/Vin = Wp^2 / ( +Wp^2 + Wp*2*Dp*s + s^2 )

In the second step of the transformation, we replace s by (g*(1-z^-1)/(1+z^-1)). Where g is 2 divided by the sampling rate of the discrete filter.

s = (g*(1-z^-1)/(1+z^-1))
H(z) = Wp^2 / ( +Wp^2 + Wp*2*Dp*(g*(1-z^-1)/(1+z^-1)
     + (g*(1-z^-1)/(1+z^-1))^2 )

This leads to the discrete transfer function as shown above. The only remaining part of the exercise is to transform the equation in such a way that the filter coefficients for the numerator and dominator are obvious. This is not difficult but still an error-prune process, so you need to pay close attention.

H(z) = Wp^2*(1+z^-1)^2 / ( Wp^2*(1+z^-1)^2
        + Wp*2*Dp*g*(1-z^-1)*(1+z^-1)) + (g*(1-z^-1)^2 )
H(z) = Wp^2 + 2*Wp^2*(z^-1) +Wp^2*(z^-2) /
        ( Wp^2 + 2*Wp^2*(z^-1) +Wp^2*(z^-2) + Wp*2*Dp*g
        - Wp*2*Dp*g(z^-2) + g^2 - 2*g^2*(z^-1) + g^2*(z^-2))
H(z) = Wp^2 + 2*Wp^2*(z^-1) +Wp^2*(z^-2) /
        ( (Wp^2 + g^2 + Wp*2*Dp*g) + (2*Wp^2 - 2*g^2)*(z^-1)
        + (Wp^2 - Wp*2*Dp*g + g^2)*(z^-2))

After this conversion, the discrete filter coefficients are still available, so the filter implementation is very easy as shown in the below example.

module lpf(Vin, Vout);
  // H(z) = Wp^2 + 2*Wp^2*(z^-1) +Wp^2*(z^-2) /
  // ( (Wp^2 + g^2 + Wp*2*Dp*g) + (2*Wp^2 - 2*g^2)*(z^-1)
  // + (Wp^2 - Wp*2*Dp*g + g^2)*(z^-2))

  output Vout;
  wreal Vout;
  input Vin;
  wreal Vin;

  parameter real hfs = 1G; // Filter sampling frequency
  parameter real Fp = 10M;
  parameter real Dp = 0.5;

  // LOCAL VARIABLES
  real Ts, g, Wp;
  real num0, num1, num2;
  real den0, den1, den2;
  real yn0, yn1, yn2;
  real xn0, xn1, xn2;

  initial begin
    Wp = `M_TWO_PI*Fp;
    yn2 = 0;
    yn1 = 0;
    yn0 = 0;
    xn2 = 0;
    xn1 = 0;
    xn0 = 0;

  // Filter intermediate variables and coefficients calculation
  //
  Ts= 1.0E+9/hfs; // in ns
  g = 2.0*hfs; // 2/Ts in sec

  // numerator
  num0 = (Wp**2);
  num1 = + 2*Wp**2;
  num2 = +Wp**2;

  // denominator
  den0 = (Wp**2 + g**2 + Wp*2*Dp*g);
  den1 = (2*Wp**2 - 2*g**2);
  den2 = (Wp**2 - Wp*2*Dp*g + g**2);
  end // always begin

  always @(Vin) begin
    // Signal flow graph intermediate values
    xn2 = xn1;
    xn1 = xn0;
    xn0 = Vin;
    yn2 = yn1;
    yn1 = yn0;
    yn0 = ((num0*xn0) + (num1*xn1) + (num2*xn2)
      - (den1*yn1 + den2*yn2) )/den0 ;
    end
    assign Vout = + yn0;
endmodule

The following figure shows the results of the low pass filter examples. The output results of the analog and discrete filter implementation are almost identical.

Event-based and Fixed Sampling Time

In the example given in the Low Pass Filter section, the input signal is first converted into a fixed rate sampled data. The filter block is triggered by these events in the always @(Vin) section. This ensures that the filter block is triggered right after the sampling event and a series of filters would trigger one after another in the sequential order.

However, the sampling frequency is a parameter to the filter block itself that is used to calculate the filter coefficients. A mismatch between the actual sampling rate and the filter parameter setting would result in wrong output results. A measurement of the sampling rate inside the filter replacing the instance parameter might be a useful enhancement to overcome this limitation.

The other task would be to change the always block triggering to a time-based trigger, such as always #Ts begin. This would ensure that the filter input is sampled at the right rate and that it is not driven accidentally by a flexible sampling rate. However, the comments above about choosing the right sampling rate still apply.

Furthermore, if a chain of filters triggers at the same time point then the sequence of execution of these filters is unpredictable. Thus, it might be that the sequence of blocks is executed in the wrong order.

Generally, event-based modeling and modeling with fixed sample times are both adequate measures in real value modeling. However, we need to choose the appropriate technique while considering different cases.

ADC/DAC Example

The following is an example of an ADC and a DAC combination. The testbench is creating a different kind of input stimuli including a ramp and a sinusoidal waveform similar to the value sources discussed earlier. In addition, VDD, VSS, and a CLK value are created.

`timescale 1ns/1ps
`define Nbits 12
`include "constants.vams"
`include "disciplines.vams"

module top ();
wreal AIN, AOUT, VDD, VSS;
wire [`Nbits-1:0] DOUT;
wrealADC I_ACD (DOUT,AIN,CK,VDD,VSS);
wrealDAC I_DAC (AOUT,DOUT,CK,VDD,VSS);

real r_ain, r_vdd, r_vss;       // input voltage
real Freq=600K,Phase=0;         // sinusoid params
reg     clk;

initial begin
  clk = 0;
  r_vdd = 3.3;
  r_vss = 0.0;
  r_ain=0.1;            // out=0.1 for DC op point
  repeat (10) #10 r_ain=r_ain+0.348;    // increasing ramp
  repeat (10) #10 r_ain=r_ain-0.339;    // falling ramp
  #30 while ($abstime<6000p) begin
                               // generate ramped sine input
  #2 Freq=Freq*1.0007;         // gradual freq increase
  Phase=Phase+2n*Freq;         // integrate freq to get phase
  if (Phase>1) Phase=Phase-1;  // wraparound per cycle
  r_ain=1.8*(1+sin(`M_TWO_PI*Phase));
           // sinusoidal waveform shape
  end
  #200 $finish; // done with test
end

always #2 clk = ~clk;

assign AIN = r_ain;
assign CK = clk;
assign VDD = r_vdd;
assign VSS = r_vss;

endmodule  

The wreal input signals are passed to an ADC block. The first always block calculates the lower and upper limits for the output values and the input value precision (PerBit) given by the input value swing and the number of output bits. These values are updated, whenever the supply values are changing.

The second always block is triggered on every change of the clock. If the input value does not exceed the lower or upper limits, the value is divided by the PerBit value providing the output value in the integer format. This value is assigned to the output bus after a given delay.

module wrealADC (DOUT,AIN,CK,VDD,VSS);
  output [`Nbits-1:0] DOUT;
  input           CK;
  input           AIN,VDD,VSS;
  wreal     AIN,VDD,VSS;
  parameter          Td=1n;
  real           PerBit, VL, VH;
  integer           Dval;
  always begin     // get dV per bit wrt supply
    PerBit = (VDD-VSS) / ((1<<`Nbits)-1);
    VL = VSS;
    VH = VDD;
    @(VDD,VSS);    // update if supply changes
  end
  always @(CK) begin
    if (AIN<VL) Dval = 'b0;
    else if (AIN>VH) Dval = {`Nbits{1'b1}};
    else Dval = (AIN-VSS)/PerBit;
  end
  assign #(Td/1n) DOUT = Dval;
endmodule // wrealADC

The inverse digital to analog (or wreal in this case) operation is even simpler. The first always block is almost identical and the second just multiplies the output precision value with the input value bus. The resulting value is assigned to the wreal output wire.

module wrealDAC (AOUT,DIN,CK,VDD,VSS);
  input [`Nbits-1:0] DIN;
  input           CK,VDD,VSS;
  output           AOUT;  
  wreal     AOUT,VDD,VSS;
  parameter real     Td=1n;
  real           PerBit,Aval;
  always begin     // get dV per bit wrt supply
    PerBit = (VDD-VSS) / ((1<<`Nbits)-1);
    @(VDD,VSS);    // update if supply changes
  end
  always @(CK) Aval <= VSS + PerBit*DIN;
  assign #(Td/1n) AOUT = Aval;
endmodule // wrealDAC

The following figure shows the simulation results for the above test case.




 ⠀
X