Previous Section Next Section

4.6 Units

Originally, all objects in e were defined using the struct keyword. A problem with this approach was that if a verification environment was built for a certain level of HDL hierarchy, the environment could not be easily ported to another level of HDL hierarchy without significant changes to the e code. This led to the introduction of the unit keyword in the e language.

4.6.1 Unit Overview

Units are the basic structural blocks for creating verification components (verification cores) that can easily be integrated together to test larger and larger portions of an HDL design as it develops. Units, like structs, are compound data types that contain data fields, procedural methods, and other members. However, units are intended as main topology entities. They have some additional features that are not needed for structs.

Unlike structs, a unit instance may be bound to a particular component in the DUT (an HDL path). Furthermore, each unit instance has a unique and constant place (an e path) in the run-time data structure of an e program. Both the e path and the complete HDL path associated with a unit instance are determined during pre-run generation (pre-run generation will be explained in detailed in Chapter 11). Thus a unit instance can be moved to a different level of verification hierarchy by simply changing the HDL path of the unit instance.

4.6.2 Units vs. Structs

The decision of whether to model a DUT component with a unit or a struct often depends on your verification strategy. Compelling reasons for using a unit instead of a struct include:

On the other hand, using a struct to model abstract collections of data, like packets, allows you more flexibility as to when you generate the data. With structs, you can generate the data during pre-run generation, at runtime, or on the fly, possibly in response to conditions in the DUT. Unit instances, however, can only be generated during pre-run generation. Figure 4-2 shows the differences between structs and units.

Figure 4-2. Comparison between Structs and Units

graphics/04fig02.gif

4.6.3 Defining Units

Units are the basic structural blocks for creating verification components (verification cores) that can easily be integrated together to test larger designs. Units are a special kind of struct, with two important properties:

Units are defined with the keyword unit. The syntax definition of a unit is as follows:

unit unit-type [like base-unit-type] {
      [unit-member; ...]};

The components cited in the above unit definition are explained in further detail in Table 4-9 below.

Table 4-9. Components of a Unit Definition

unit-type

Denotes the type of the new unit.

base-unit-type

Denotes the type of the unit from which the new unit inherits its members.

unit-member; …

Denotes the contents of the unit. Like structs, units can have the following types of members:

  • data fields for storing data

  • methods for procedures

  • events for defining temporal triggers

  • coverage groups for defining coverage points

  • when, for specifying inheritance subtypes

  • declarative constraints for describing relationships between data fields

  • on, for specifying actions to perform upon event occurrences

  • expect, for specifying temporal behavior rules

Unlike structs, units can also have verilog members. The definition of a unit can be empty, containing no members.

Example 4-11 illustrates a simple unit definition.

Example 4-11 Basic Unit Definition
Example of unit definition
<'
unit router_channel { //Definition of unit router_channel
   //Examples of struct members that can be defined in a unit
   //At this point, the syntax of these struct members is not relevant
    event external_clock;
    event packet_start is rise('valid_out')@sim;
    event data_passed;

    verilog variable 'valid_out' using wire;

    data_checker() @external_clock is {
        while 'valid_out' == 1 {
        wait cycle;
        check that 'data_out' == 'data_in';
        };
    emit data_passed;
    };

    on packet_start {
        start data_checker();
    };
}; //End of unit definition

'>

4.6.4 HDL Paths

Relative HDL paths are essential in creating a verification module that can be used to test a DUT component either standalone or integrated into different or larger systems. Binding an e unit instance to a particular component in the DUT hierarchy allows you to reference signals within that DUT component using relative HDL path names. Regardless of where the DUT component is instantiated in the final integration, the HDL path names are still valid.

To illustrate this concept, let's look at the fast_router shown in Figure 4-3. This figure shows the e hierarchy with sys at the topmost level.

Figure 4-3. e Hierarchy of the fast_router

graphics/04fig03.gif

Each unit instance in the unit instance tree of the fast_router matches a module instance in the Verilog DUT, as shown in Figure 4-4. The one-to-one correspondence in this particular design between e unit instances and DUT module instances is not required for all designs. In more complex designs, there may be several levels of DUT hierarchy corresponding to a single level of hierarchy in the tree of e unit instances.

Figure 4-4. DUT Router Hierarchy

graphics/04fig04.gif

To associate a unit or unit instance with a DUT component, you use the hdl_path() method within a keep constraint. Example 4-12 extends sys by creating an instance of the XYZ_router unit and binds the unit instance to the "router_i" instance in the DUT.

Example 4-12 HDL Path for the fast_router
Example of a fast_router with hdl_path() binding
to an HDL hierarchy. All simulator signal accesses
are now done relative to the hdl_path()
<'
unit fast_router { //Define a fast_router unit. This is only a
                   //simple example. Actual fast_router code will
                   //contain lots of verification code.
    debug_mode: bool;
};

extend sys {
    unit_core: fast_router is instance; //Instantiate the fast_router
    keep unit_core.hdl_path() =="top.router_i"; //Associate this
                                       // instance with the HDL design
    keep unit_core.debug_mode == TRUE; //Constraint to set debug mode
};
'>

Similarly, Example 4-13 creates three instances of router_channel in fast_router and constrains the HDL path of the instances to be "chan0", "chan1", "chan2". These are the names of the channel instances in the DUT relative to the "router_i" instance.

Example 4-13 HDL Path for the Channels
Example showing how the channels are instantiated
inside the fast_router and the assignment of hdl_path()
for those channels.
<'
unit router_channel { //Code in this unit not shown
};

unit fast_router { //Definition of fast router
    channels: list of router_channel is instance;//Instantiate
                                                 //channels
    keep channels.size() == 3; //There are 3 channels
    keep for each in channels { //Set the hdl_path() for each channel
      .hdl_path() == append("chan", index); };
};
'>

The full HDL path of each unit instance is determined during pre-run generation, by appending the HDL path of the child unit instance to the full path of its parent, starting with the sys hierarchy. sys has the empty full path "". Thus the full path for the fast_router instance is "top.router_i" and that for the first channel instance is "top.router_i.chan0".

4.6.5 Predefined Methods for Units

Besides hdl_path() there is another predefined method for units called get_enclosing_unit() that is commonly used.

4.6.5.1 get_enclosing_unit()

This predefined method of a unit returns a reference to the nearest higher-level unit instance of the specified type, allowing you to access fields of the parent unit in a typed manner. The syntax for this predefined method is as follows:

[exp.]get_enclosing_unit(unit-type: exp): unit instance;

The arguments to this method are shown in Table 4-10 below.

Table 4-10. Arguments for get_enclosing_unit() Method

exp

An expression that returns a unit or a struct. If no expression is specified, the current struct or unit is assumed.

unit-type

The name of a unit type or unit subtype.

Example 4-14 shows the usage of this method.

Example 4-14 Usage of get_enclosing_unit()
unpack(p.get_enclosing_unit(fast_router).pack_config,
       'data', current_packet);
4.6.5.2 Other Predefined Methods

Many other predefined methods are available with unit definitions. Please refer to the e Language Reference Manual for details.

Previous Section Next Section