Multiple components make up a verification environment. Figure 11-1 shows the components in a typical verification environment. This section describes how various verification components are built and the e constructs that are needed to build these verification components.
The information provided in this section is a guideline for creation of verification environments. Actual verification environments will vary slightly depending on the needs of the particular project.
The generation aspect of verification contains data objects. These data objects are structs that represent the abstract form of input stimulus. Data objects can be built with the following e syntax.
Data objects are declared as struct, not unit. Thus, data objects can be generated on-the-fly using the gen action.
Constraints are defined using keep and keep for each constructs. Constraints ensure that legal stimulus is generated.
Usually, there is one data object per input protocol. If the DUT handles multiple input protocols, each has a data object corresponding to the stimulus of that protocol.
The driver object performs the function of taking the stimulus data object one instance at a time and applying it to the DUT until all stimulus has been applied. Typically, the interface object is similar to an HDL bus functional model. In case of a networking environment, it is also known as a port object. Figure 11-2 shows the typical design of a driver object.
There can be more than one driver object in the design. Typically, there is one driver object per input interface. If there are multiple input protocols supported by the DUT, each has its own driver object. Here are some characteristics of driver objects:
Driver objects are declared as unit. A keep constraint on the hdl_path() determines the HDL hierarchy instance to which that driver object is attached.
Driver objects are typically written once per project.
A drive() TCM with a sampling event defined as the clock of the input interface is responsible for driving one stimulus data object onto the input bus. The pack() method is used to convert an abstract data object into a list of bits or bytes.
A gen_drive() TCM with a sampling event equal to the clock of the input protocol is responsible for generating many such stimulus data objects (using the gen action or a list) and applying them one by one to the DUT by calling the drive() TCM.
The receiver object is responsible for collecting the data at the output of the DUT. The receiver object performs the function of taking the raw output data from the DUT based on the output protocol and converting it to an abstract data object for comparison. Typically, the receiver object is similar to an HDL bus functional model. Figure 11-3 shows the typical design of a receiver object.
There can be more than one receiver object in the design. Typically, there is one receiver object per output interface. If there are multiple output protocols supported by the DUT, each has its own receiver object. In certain protocols, the driver and the receiver objects might be combined into one object. Here are some characteristics of receiver objects:
Receiver objects are declared as unit. A keep constraint on the hdl_path() determines the HDL hierarchy instance to which that receiver object is attached.
Receiver objects are typically written once per project.
A collect() TCM with a sampling event equal to the clock of the output protocol is responsible for receiving one stimulus data object on the output bus. The unpack() method is used to convert a list of bits or bytes received on the output bus into an abstract data object.
The output data object is then passed to the data_checker object for comparison against expected values.
The data_checker object stores expected values for each input data object injected into the DUT. The expected values are computed by applicaion of the transformation algorithm to the input data objects. This transformation algorithm is identical to the one applied to the input stimulus in the DUT. When the receiver object receives one data object, it passes it to the data_checker for comparison against the expected values. Figure 11-4 shows the typical design of a data_checker object.
Data_checker objects may be instantiated in the receiver object. In such cases, there is one data_checker object per receiver object. Here are some characteristics of data_checker objects:
Data_checker objects are declared as unit. A keep constraint on the hdl_path() determines the HDL hierarchy instance to which that data_checker object is attached.
Data_checker objects are typically written once per project.
An add() TCM with a sampling event equal to the clock of the output protocol is responsible for adding one stimulus data object received on the output bus for comparison against its expected value.
A compare() TCM with a sampling event equal to the clock of the output protocol is responsible for comparing one stimulus data object received on the output bus against its expected value. The predefined list methods and deep_compare_physical() method are called to perform this comparison.
Scoreboarding is a common technique to perform such comparisons. Scoreboarding is a general verification technique. It is not specific only to e-based verification. Figure 11-5 shows a typical scoreboarding methodology that can be developed using e. In Figure 11-5, the data item is added to the scoreboard as soon as it is applied to the input of the DUT. When the output is received, the output is compared to the data item on the scoreboard.
A monitor object checks the input and output timing protocol. Here are some characteristics of a monitor object:
Monitor objects are declared as unit. A keep constraint on the hdl_path() determines the HDL hierarchy instance to which that monitor object is attached.
Monitor objects are typically written once per project.
Monitor objects are written with expect struct members.
A dut_error() method is called if the expect fails.
A monitor object may be built with multiple lower level objects.
A coverage object sets up coverage tracking on key items in the e code and in the DUT. Some coverage objects can also be embedded in the extension to a driver or receiver object. Here are some characteristics of a coverage object:
Coverage objects are declared as unit. A keep constraint on the hdl_path() determines the HDL hierarchy instance to which that coverage object is attached.
Coverage objects are typically written once per project.
Coverage objects are written with cover struct members.
Tests are derived from the test plan. In e, tests are simply extensions of existing struct and unit definitions that impose additional constraints on these objects. Test files are very small and are easy to write and to maintain.
Often, a verification environment that is designed for a module will be needed at the chip level and at the system level. All verification components discussed in this section should be automatically transported to a different level of verification. Therefore, instead of directly instantiating these components in sys, create an intermediate level of hierarchy called env under sys. All verification components are instantiated under env. Thus, one can transport env to any other level of hierarchy without having to transport each individual verification component. Figure 11-6 shows the hierarchy with the env object.