Product Documentation
Cadence Verilog-AMS Language Reference
Product Version 22.09, April 2022

2


Creating Modules

A module definition appears between the keywords module and endmodule or macromodule and endmodule. The following definition for a digital to analog converter illustrates the form of a module definition.

See the following topics for information about creating and using modules in Cadence® Verilog®-AMS:

Declaring Modules

To declare a module, use this syntax.

module_declaration ::= 
     module_keyword module_identifier [ ( list_of_ports ) ] ;      [ module_items ]      endmodule
module_keyword ::=
        module
|   macromodule
module_items ::=
        { module_item }
    |   analog_block

module_item ::= 
        module_item_declaration
    |   parameter_override
    |   module_instantiation
    |   digital_continuous_assignment
    |   digital_gate_instantiation
    |   digital_udp_instantiation
    |   digital_specify_block
    |   digital_initial_construct
    |   digital_always_construct
module_item_declaration ::=
        parameter_declaration
    |   aliasparam_declaration
    |   input_declaration
    |   output_declaration
    |   inout_declaration
    |   ground_declaration
    |   integer_declaration
    |   net_discipline_declaration
    |   real_declaration
    |   genvar_declaration
    |   branch_declaration
    |   analog_function_declaration
    |   digital_function_declaration
    |   digital_net_declaration
    |   digital_reg_declaration
    |   digital_time_declaration
    |   digital_realtime_declaration
    |   digital_event_declaration
    |   digital_task_declaration
parameter_override ::=
        defparam list_of_param_assignments ;

module_identifier

The name of the module being declared.

list_of_ports

An ordered list of the module’s ports. For details, see “Ports”.

module_items

Declarations and definitions.

Note that you can have no more than one analog block in each module.

For information about Read

Analog blocks

“Defining Module Analog Behavior”

Parameter overrides

“Overriding Parameter Values in Instances”

Module instantiation

“Instantiating Verilog-AMS Modules”

Digital continuous assignments

“Continuous Assignments” in the Verilog-XL Reference

Digital gate instantiations

“Gate and Switch Declaration Syntax” in the Verilog-XL Reference

Digital udp instantiations

“UDP Instances” in the Verilog-XL Reference

Digital specify blocks

“Understanding Specify Blocks” in the Verilog-XL Reference

Digital initial constructs

“initial Statement” in the Verilog-XL Reference

Digital always constructs

“always Statement” in the Verilog-XL Reference

Parameter declarations

“Parameters”

Input, output, and inout declarations

“Port Direction”

Ground declarations

“Ground Nodes”

Integer declarations

“Integer Numbers”

Net discipline declarations

“Net Disciplines”

Real declarations

“Real Numbers”

Genvar declarations

“Genvars”

Branch declarations

“Named Branches”

Analog function declarations

“User-Defined Functions”

Digital function declarations

“Functions and Function Calling” in the Verilog-XL Reference

Digital net declarations

“Net and Register Declaration Syntax” in the Verilog-XL Reference

Digital reg declarations

“Net and Register Declaration Syntax” in the Verilog-XL Reference

Digital time declarations

“Integers and Times” in the Verilog-XL Reference

Digital realtime declarations

“Real Numbers” in the Verilog-XL Reference

The simulator evaluates realtime and real declarations identically.

Digital event declarations

“Event Control” in the Verilog-XL Reference

Digital task declarations

“Tasks and Task Enabling” in the Verilog-XL Reference

Declaring the Module Interface

Use the module interface declarations to define

For example, the module interface declaration

module res(p, n) ;
inout p, n ;
electrical p, n ;
parameter real r = 0 ;

declares a module named res, ports named p and n, and a parameter named r.

Module Name

To define the name for a module, put an identifier after the keyword module or macromodule. Ensure that the new module name is unique among other module, schematic, subcircuit, and model names, and any built-in Spectre® circuit simulator primitives. If your module has any ports, list them in parentheses following the identifier.

Ports

To declare the ports used in a module, use port declarations. To specify the type and direction of a port, use the related declarations described in this section.

list_of_ports ::= 
         port { , port } 

port ::= 
         port_expression
    |    .port identifier( [port_expression ])
port_expression ::=      port_identifier     | port_identifier [ constant_expression ]     | port_identifier [ constant_range ] constant_range ::=     msb_constant_expression : lsb_constant_expression

For example, these code fragments illustrate possible port declarations.

module exam1 ;               // Defines no ports
module exam2 (p, n) ;        // Defines 2 simple ports

Normally, you cannot use Q as the name of a port. However, if you need to use Q as a port name, you can use the special text macro identifier, VAMS_ELEC_DIS_ONLY, as follows.

`define VAMS_ELEC_DIS_ONLY
`include "disciplines.vams"
(module 1, which uses a port called Q)
(module 2, which use a port called Q)
...
`include "disciplines.vams"
(module 3, which uses an access function called Q)
(module 4, which uses an access function called Q)
...

This macro undefines the sections in the disciplines.vams file that use Q, making it available for you to use as a port name. Consequently, when you need to use Q as an access function again, you need to include the disciplines.vams file again.

module exam5 (.b(p), .d(n))  // Defines the ports b and d, which are
// connected to the signals p and n,
// respectively

Port Type

To declare the type of a port, use a net discipline declaration in the body of the module. If you do not declare the type of a port, you can use the port only in a structural description. In other words, you can pass the port to module instances, but you cannot access the port in a behavioral description. Net discipline declarations are described in “Net Disciplines”.

Ports declared as vectors must use identical ranges for the port type and port direction declarations.

Port Direction

You must declare the port direction for every port in the list of ports section of the module declaration. To declare the direction of a port, use one of the following three syntaxes.

input_declaration ::=
         input [ range ] list_of_port_identifiers ;
output_declaration ::=      output [ range ] list_of_port_identifiers ;
inout_declaration ::=      inout [ range ] list_of_port_identifiers ;
range ::=      [ constant_expression : constant_expression ]

Ports declared as vectors must use identical ranges for the port type and port direction declarations.

In this release of Verilog-AMS,

Port Declaration Example

Module daconv, described below, has nine ports. The compSig port is declared with a port direction of output, so that its value can be set. The other ports are declared with a port direction of input, so that their values can be read. The compSig port is declared as an analog port of the electrical discipline.

module daconv(b0, b1, b2, b3, b4, b5, b6, b7, compSig); // Declares nine ports
input b0, b1, b2, b3, b4, b5, b6, b7;        // Declares ports as input
output compSig;                              // Declares port as output

logic b0, b1, b2, b3, b4, b5, b6, b7;        // Declares type of digital ports
electrical compSig;                          // Declares type of analog port

parameter real refVolt = 12.0;

analog 
    begin

        V(compSig) <+  (refVolt/256) *(b0 + 2*(b1 + 2*(b2 + 2*(b3 +2*(b4 +2*
            (b5 +2*(b6 +2*b7)))))));

    end
endmodule

Parameters

With parameter (and dynamicparam) declarations, you specify parameters that can be changed when a module is used as an instance in a design. Using parameters lets you customize each instance.

For each parameter, you must specify a default value. You can also specify an optional type and an optional valid range. The following example illustrates how to declare parameters and variables in a module.

Module sdiode has a parameter, area, that defaults to 1. If area is not specified for an instance, it receives a value of 1. Similarly, the other parameters, is, n, cjo, m, phi, and tt, have specified default values too.

Module sdiode also defines three local variables: vd, id, and qd.

For more information about parameter declarations, see “Parameters”.

Specifying Supply Sensitivity Attributes

Add the groundSensitivity and supplySensitivity attributes to a port or pin definition in a mattributesodule to make a connect module sensitive to supplies in the module to which it connects.

sensitivity_attribute ::= 
        (* [ integer groundSensitivity = "gSig_sensitive_to" ; ] 
   [ integer supplySensitivity = "sSig_sensitive_to" ; ] *)

gSig_sensitive_to, sSig_sensitive_to
Names of signals, typically global signals, to which you want a connect module to be sensitive.

When you specify a supplySensitivity or a groundSensitivity attribute on a signal in a connect module, the declared signal (in the connect module) takes on the value of the supplySensitivity or groundSensitivity signal you specify.

When you specify a supplySensitivity or the groundSensitivity attribute (or both) on a signal in an ordinary module, the value of the supplySensitivity or groundSensitivity signal overrides the value of the signal of the same name in the connect module to which the ordinary module connects.

For example, you might use the groundSensitivity attribute in a connect module (such as l_to_e, below) as follows:

connectmodule l_to_e(dval, aval);
...
electrical (* integer groundSensitivity = "global_pwr.pow1" ; *) gnd ;
...
endmodule

The default value of gnd in this connect module is the value of signal global_pwr.pow1. If module l_to_e connects to digital port d in ordinary module sample (below), the value of global_pwr.pow5, which appears in the groundSensitivity attribute for d, overrides the default value of gnd in the connect module.

module sample(d);
output (* integer groundSensitivity = "global_pwr.pow5" ; *) d ;
...
endmodule

If port d does not have a groundSensitivity attribute, the value of gnd in the connect module retains its default value of global_pwr.pow1:

module sample(d);
output d ;
...
endmodule

Making a connect module sensitive to supplies in the connected digital port is more likely to produce the behavior you expect because:

The following basic principles apply to using these sensitivity attributes:

See the following topics for more information:

Using the Sensitivity Attributes in a Chain of Buffers

Consider a design containing three buffers, such as the following, where buffers ba1 and ba3 are analog blocks with analog input and output pins, and buffer bd2 is a digital block with logic input and output pins:

During elaboration, the software inserts connect modules across net n1 and the digital input port of buffer bd2, and across the digital output port of buffer bd2 and net n2.

If you know this design has an operating voltage of 5.0 Volts, you might write an analog-to-digital connect module with hard-coded thresholds, such as the following:

‘include "disciplines.vams"
connectmodule elect2logic(aVal, dVal);
output dVal;
input aVal;
logic dVal;
electrical aVal;
reg temp;
always begin // Digital, do this always
if(V(aVal) > 3.0)
#1 temp = 1; // Delay 1 time unit, drive output 1
else if (V(aVal) < 2.0)
#1 temp = 0; // or drive output 0, depending on aVal
else
#1 temp = 1’bx;
end
assign dVal = temp; // Bind register to digital output
endmodule

However, if the design can operate at 3.0 or 5.0 Volts depending on the supplies, you might use the supplySensitivity and groundSensitivity attributes to write a connect module that is sensitive to the supplies, such as the following:

// Supply-sensitive connect module 
‘include "disciplines.vams"
connectmodule elect2logic(aVal, dVal);
output dVal;
input aVal;
logic dVal;
electrical aVal;
electrical (* integer supplySensitivity = "cds_globals.\\vdd! " ; *) \vdd! ;
electrical (* integer groundSensitivity = "cds_globals.\\vss! " ; *) \vss! ;
reg temp;
always begin // Do this always
if(V(aVal) > ((V(\vdd! ) - V(\vss! ))/2 + 0.5 ))
#1 temp = 1; // Delay 1 time unit, drive output 1
else if (V(aVal) < ((V(\vdd! ) - V(\vss! ))/2 -0.5))
#1 temp = 0; // or drive output 0, depending on aVal
else
#1 temp = 1’bx;
end
assign dVal = temp; // Bind register to digital output
endmodule

The threshold values are functions of the supply and ground values.

To specify the digital ports to which the connect module is sensitive, add groundSensitivity and supplySensitivity attributes to the connected digital port. In our example, the software inserts a connect module both at the input and at the output port of buffer bd2, so in the supply-sensitive module definition, you would add both sensitivity attributes to both ports of the buffer, like this:

module bux2_5V (Z,A);
input
(* integer supplySensitivity="\\vdd! ";
integer groundSensitivity="\\vss! "; *)
A ;
output
(* integer supplySensitivity="\\vdd! ";
integer groundSensitivity="\\vss! "; *)
Z;
wire \vss! ;
wire \vdd! ;
analog begin
V(\vss! ) <+ 0.0 ;
V(\vdd! ) <+ 5.0 ;
end
buf #1 (Z,A);
specify
specparam
t_A_Z_rise = 0.1,
t_A_Z_fall = 0.1;
// Delays
(A +=> Z) = (t_A_Z_rise,t_A_Z_fall);
endspecify
endmodule

Using Sensitivity Attributes with Inherited Connections

An inherited connection is a net expression associated with either a signal or a terminal. You use inherited connections to override specific global names in your design. For more information, see “Inherited Connections” in the Virtuoso Schematic Editor L User Guide.

You can use inherited connections to set the values of signals on ports and use the supply sensitivity attributes to make a connect module sensitive to those values. By doing so, you can switch between different power supplies (that you set by inherited connections) and have connect modules that behave differently depending on the value of the supplies.

For example, here is a buffer module with both supply sensitivity attributes on both the input and the output ports (A and Z). The signal name for each of the sensitivity attributes is an inherited connection (\\vdd! for supplySensitivity and \\vss! for groundSensitivity). The inh_conn_prop_name and inh_conn_def_value attributes on wires \vss! and \vdd! set the value of the inherited connections:

module bux2 (Z,A);
input
(* integer supplySensitivity="\\vdd! ";
integer groundSensitivity="\\vss! "; *)
A ;
output
(* integer supplySensitivity="\\vdd! ";
integer groundSensitivity="\\vss! "; *)
Z;
wire
(* inh_conn_prop_name="lSup", // if set, specifies value for \vss!
inh_conn_def_value="cds_globals.\\vss! " *)
\vss! ; // \vss! has default value cds_globals.\\vss!
wire
(* inh_conn_prop_name="hSup", // if set, specifies value for \vdd!
inh_conn_def_value="cds_globals.\\vdd! " *)
\vdd! ; // \vdd! has default value cds_globals.\\vdd!
buf #1 (Z,A);
‘ifdef functional
‘else
specify
specparam
t_A_Z_rise = 0.1,
t_A_Z_fall = 0.1;
// Delays
(A +=> Z) = (t_A_Z_rise,t_A_Z_fall);
endspecify
‘endif
endmodule

You can compare this buffer module with module bux2_5V in the previous section, where the \vss! and \vdd! net values do not depend on inherited connections:

wire \vss! ;
wire \vdd! ;

The supply-sensitive connect module is the same as the one that appears in the previous section.

Defining Module Analog Behavior

To define the analog (continuous time) behavioral characteristics of a module, you create an analog block. The simulator evaluates all the analog blocks in the various modules of a design as though the blocks are executing concurrently.

analog_block ::= 
        analog analog_statement
analog_statement ::=    analog_seq_block
|analog_branch_contribution
|analog_indirect_branch_assignment
|analog_procedural_assignment
|analog_conditional_statement
|analog_for_statement
|analog_case_statement
|analog_event_controlled_statement
|system_task_enable
|statement
statement ::=    seq_block
|procedural_assignment
|conditional_statement
|loop_statement
|case_statement

analog_statement can appear only within the analog block.

statement can appear anywhere within the module, including within the analog block.

See “Sequential Block Statement” for more information about analog_seq_block and seq_block.

In the analog block, you can code contribution statements that define relationships among analog signals in the module. For example, consider the following contribution statements:

V(n1, n2) <+ expression;
I(n1, n2) <+ expression;

where V(n1,n2) and I(n1,n2) represent potential and flow sources, respectively. You can define expression to be any combination of linear, nonlinear, algebraic, or differential expressions involving module signals, constants, and parameters.

The modules you write can contain at most a single analog block. When you use an analog block, you must place it after the interface declarations and local declarations.

Because the description in the analog block is a continuous-time behavioral description, you must not use blocking event control statements, such as blocking delays, events, or waits, within the block.

The following module includes an analog block and initial and always blocks. These blocks work together within a single module to define an analog to digital converter.

module adc;
electrical vin;
parameter real a_amp = 5; // This parameter is used by analog.
parameter real d_volt_range = 5; // This parameter is used by digital.
real a_freq, a_phase;
real d_half_range;
real d_vin;
real a_vin
real d_vin_save;
reg [7:0] b;
integer ii; integer d_fd;
initial begin
b = 0;
d_half_range = d_volt_range / 2;
d_fd = $fopen("ms6.dat");
$fstrobe(d_fd,"time\tb\td_vin\ta_vin\n");
d_vin = 0;
end
always begin
#1;
d_vin = V(vin); // Probes the voltage.
d_vin_save = d_vin;
   for (ii=0; ii < 8; ii = ii + 1) begin // Converts the voltage into
// an 8-bit register.
if (d_vin > d_half_range) begin
b[ii] = 1;
d_vin = d_vin - d_half_range;
end else b[ii] = 0;
d_vin = d_vin * 2;
end
// Writes the digital output to a file.
$fstrobe(d_fd,"%g\t%b\t%g\t%g",$abstime, b, d_vin_save, a_vin);
end
analog begin
@(initial_step) begin
a_freq = 10K;
end
   // input
a_phase = 2*‘M_PI*a_freq*$abstime;
a_vin = a_amp*sin(a_phase);
V(vin) <+ a_amp*sin(a_phase); // Creates a sinusoidal voltage source.
end
endmodule

Defining Analog Behavior with Control Flow

You can also incorporate conditional control flow into a module. With control flow, you can define the behavior of a module in regions.

The following module, for example, describes a voltage deadband amplifier vdba. If the input voltage is greater than vin_high or less than vin_low, the amplifier is active. When the amplifier is active, the output is gain times the differential voltage between the input voltage and the edge of the deadband. When the input is in the deadband between vin_low and vin_high, the amplifier is quiescent and the output voltage is zero.

module vdba(in, out);
input in ;
output out ;
electrical in, out ;
parameter real vin_low = -2.0 ;
parameter real vin_high = 2.0 ;
parameter real gain = 1 from (0:inf) ;
 analog begin
if (V(in) >= vin_high) begin
V(out) <+ gain*(V(in) - vin_high) ;
end else if (V(in) <= vin_low) begin
V(out) <+ gain*(V(in) - vin_low) ;
end else begin
V(out) <+ 0 ;
end
end
endmodule

The following graph shows the response of the vdba module to a sinusoidal source.

Using Integration and Differentiation with Analog Signals

The relationships that you define among analog signals can include time domain differentiation and integration. Verilog-AMS provides a time derivative function, ddt, and two time integral functions, idt and idtmod, that you can use to define such relationships. For example, you might write a behavioral description for an inductor as follows.

module induc(p, n);
inout p, n;
electrical p, n;
parameter real L = 0;
 analog
   V(p, n) <+ ddt(L * I(p, n)) ;
endmodule

In module induc, the voltage across the external ports of the component is defined as equal to the time derivative of L times the current flowing between the ports.

To define a higher order derivative, you must use an internal node or signal. For example, module diff_2 defines internal node diff, and sets V(diff) equal to the derivative of V(in). Then the module sets V(out) equal to the derivative of V(diff), in effect taking the second order derivative of V(in).

module diff_2(in, out) ;
input in ;
output out ;
electrical in, out ;
electrical diff ;   // Defines an internal node.
 analog begin
   V(diff) <+ ddt(V(in)) ;      V(out) <+ ddt(V(diff)) ;     end
endmodule

For time domain integration, use the idt or idtmod functions, as illustrated in module integrator.

module integrator(in, out) ;
input in ;
output out ;
electrical in, out ;

    analog begin
         V(out) <+ idt(V(in), 0) ;
    end
endmodule

Module integrator sets the output voltage to the integral of the input voltage. The second term in the idt function is the initial condition.

For more information on…

see…

ddt

“Time Derivative Operator”

idtmod

“Circular Integrator Operator”

idt

“Time Integral Operator”

Using Internal Nodes in Modules

Using Verilog-AMS, you can implement complex designs in a variety of different ways. For example, you can define behavior in modules at the leaf level and use the top-level module to define the structure of the system. You can also define structure within modules by defining internal nodes. With internal nodes, you can directly define behavior in the module, or you can introduce internal nodes as a means of solving higher order differential equations that define the network.

Using Internal Nodes in Behavioral Definitions

Consider the following RLC circuit.

Module rlc_behav uses an internal node n1 and the ports in, ref, and out, to define directly the behavioral characteristics of the RLC circuit. Notice how n1 does not appear in the list of ports for the module.

module rlc_behav(in, out, ref) ;
inout in, out, ref ;
electrical in, out, ref ;
parameter real R=1, L=1, C=1 ;

    electrical n1 ;

    analog begin
         V(in, n1) <+ R*I(in, n1) ;
         V(n1, out) <+ L*ddt(I(n1, out)) ;
         I(out, ref) <+ C*ddt(V(out, ref)) ;
    end
endmodule

Using Internal Nodes in Higher Order Systems

You can also represent the RLC circuit by its governing differential equations. The transfer function is given by

In the time domain, this becomes

If you set

you can write

Module rlc_high_order implements these descriptions.

module rlc_high_order(in, out, ref) ;
inout in, out, ref ;
electrical in, out, ref ;
parameter real R=1, L=1, C=1 ;

    electrical n1 ;

    analog begin
         V(n1, ref) <+ ddt(V(out, ref)) ;
         V(out, ref) <+ V(in) - (R*C*V(n1) - L*ddt(V(n1))*C ;
    end
endmodule


Return to top
 ⠀
X