Vera Tutorial


Last updated: 20 Aug 2003

Synopsys Inc. 700 East Middlefield Road Mountain View, California 94043 Tel (650) 584-5612 • Fax (650) 584-5620 •

VERA-VS, VERA-SV, VERA-VL, VERA-HVL, VERA Verification System, Verity, Verity ToolKit, ISDB, ISDB-cycle, and PowerFault are trademarks of Synopsys Inc. Magellan, PowerSim, SimWave, and VERA are registered trademarks of Synopsys Inc. All other trademarks are the property of their respective owners. This software and the concepts embodied in it are proprietary and confidential in nature, and are not to be used, duplicated in whole or in part, reverse-engineered, modified, or disclosed in any manner, for any purpose whatsoever, without prior written permission from Synopsys Inc. Synopsys Inc. assumes no liability for any use of this software, and provides no warranty of any kind for the software, its documentation, or the correctness of the results. Receipt of this material shall be considered acceptance of the conditions specified herein.

Copyright © 1996, 1997, 1998, 1999, 2000, 2002, 2003 by Synopsys, Inc. All rights reserved.

Vera Tutorial 6.0 (2003) Aug, 2003


Table of Contents


Table of Contents
1. 2. Introduction to Vera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 System Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Memory System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 System File Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 Running the Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Arbiter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arbiter Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vera Testbench Key Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verifying the Arbiter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Memory Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Memory Controller Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verifying the Memory Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Using the Vera Debugger with the cntrlr Example . . . . . . . . . . . . . . . . . . . . . . 13 13 15 19 25 25 28 37




Memory System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Memory System Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Verifying the Memory System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

4 Table of Contents Tutorial .

Introduction to Vera 5 1. Vera has been recognized as the leading testbench automation tool by numerous customer evaluations and reviews. . it is also powerful and a lot of fun.Tutorial Chapter 1. One reason we believe our product is successful is that we put great importance on the satisfaction of our customers. Introduction to Vera Vera is a robust and thorough verification tool for design and verification engineers. If you have any questions or problems. and we listen carefully to what our customers have to say. do not hesitate to contact us by email at vera-support@synopsys. We are actively increasing the functionality and usability of our tool. Vera is not only simple to

Introduction to Vera Tutorial .6 Chapter 1.

It consists of a system bus. a centralized round-robin arbiter. Figure 2-1 shows the system block diagram. and a memory controller that controls four static SRAM devices. This chapter includes these sections: • Memory System • System File Setup 2. It discusses briefly the components of the system and describes how they interact to complete the system.Tutorial Chapter 2.1 Memory System The system used in this tutorial is a simple memory system for a two CPU machine. System Overview This chapter introduces the system used for the remainder of the tutorial. System Overview 7 2. S R A M S R A M S R A M S R A M ce0_N rdWr_N ce1_N reset MEMORY CONTROLLER ROUND-ROBIN ARBITER ce2_N request[0] CPU0 grant[0] System Bus ce3_N address data Figure 2-1 Memory System Schematic grant[1] CPU1 request[1] . It also details the basic structure of the files used for this tutorial.


Chapter 2. System Overview


Notice that the blocks labeled CPU0 and CPU1 are shaded. This is to indicate that these blocks are not part of the system under test, but rather these blocks will be modeled within our testbench. The signals shown between the CPUs and the rest of the system are the interface between the system under test and the “outside world.” The memory system consists of the SRAMs, the Memory Controller, and the Arbiter. These files are all described in the HDL files of each sub-module. The approach used to verify the memsys system is similar to most project verification flows: 1. 2. sub-modules are individually verified, sub-modules are integrated into the final design.

This “full chip” functionality is verified in the system simulation. First, in Chapter 3 of the tutorial, the arbiter sub-module is verified. To do this, the surrounding blocks in the Vera testbench are modeled. Second, in Chapter 4, the memory controller sub-module is verified. For this module level verification, both the CPU interface and the memory interfaces are designed with Vera. This gives us a chance to show some of the advanced features in Vera that are used to verify protocol based designs. Finally, Chapter 5 verifies the complete system by integrating the arbiter and controller submodules as shown in Figure 2-1 with a Vera model of the CPUs instantiated in the testbench. Several different features of Vera are used in different approaches. We also introduce Object Oriented Programming (OOP), Functional Coverage, and Interprocess Communication using Triggers and Mailboxes.


Chapter 2. System Overview



System File Setup
The tutorial’s directory structure is shown below.


sram rtl rtl

arb test rtl

cntrlr test rtl

memsys test

sram.v sram.vhd

arb.v arb.vhd arb_top.vhd

cntrlr.v cntrlr.vhd cntrlr_top.vhd

memsys.v memsys.vhd memsys_top.vhd


README: short description and file/directory index and listing of tools and versions used sram: contains the memory RTL arb: contains the submodule RTL and test directory cntrlr: contains the submodule RTL and test directory memsys: contains the top-level RTL netlist that integrates the entire memsys design and the test directory Each “rtl” directory contains both VHDL and Verilog HDL code. You will be working inside the arb, cntrlr, and memsys test directories where you will be creating your Vera testbench. Each test directory, contains the solution or testbench for each module. You can refer to this solution while creating your own testbench. The diagram below, shows the general “test” directory structure for each module. include: contains Vera interface and ports and binds files for arb and memsys source: contains Vera testbench running both Verilog and VHDL code vera_out: compilation and runtime generated files go here run_scr: cshell and make run scripts for various simulators are stored here


Chapter 2. System Overview


current test directory

Makefile setup





interface.vri port_binds.vri


empty before simulation


You use the test directory for creating testbench, run scripts, compilation, and simulation generated files and directories. The files contained in the include, source, and “run_scr” directories are tutorial solutions.


Running the Tutorial
Before running the tutorial you should customize the setup script found in the current test directory. The test directory can be found in new_memsys/PROJECT_DIR/test. Where PROJECT_DIR is either arb, cntrlr, or memsys. Within the setup script you will be setting tool specific settings for Vera and your simulator. After you have finished editing, source the setup file: >source setup The tutorial contains a combination of makefiles and run scripts for execution of the tutorial solutions that run on the supported simulators shown in Table 2. The options for invoking the simulation are best handled from the Makefile included in new_memsys/PROJECT_DIR/test. This Makefile abstracts the commands for all supported simulators:

Table 1: Supported Simulators
Platform VCS VCS-MX MTI VHDL MTI Verilog NC Verilog

Solaris 5.7, 5.8 RedHat 7.2






System Overview 11 The Makefile is compatible with both make and gmake.Tutorial Chapter 2. Note – The Makefile will run the solution files not your custom file. Examples of Usage: To run the solution for VCS: >gmake cleanall >gmake vcs To run the solution for MTI VHDL: >make cleanall >make mti_build >make mti_vhdl . For example from within new_memsys/memsys/test: make -help Vera Tutorial Makefile Note – You must edit and source setup to customize your environment General Options: make help : displays this message make clean :cleans all files created during compilation and runtime make cleanall :cleans all files created during compilation and runtime Synopsys Simulation make vcs : run with VCS (Verilog) make vcs-mx : run with VCS-MX (VHDL) MTI Simulation make mti_vlog : run with MTI (Verilog) make mti_vhdl : run with MTI (VHDL) NC Simulation make nc-vlog : run with NC (Verilog) Coverage Report Options make html : generates coverage report and opens under Netscape. To see the list of options invoke the Makefile with either make/gmake or make –help / gmake –help. make text : generates coverage report and opens under more.

System Overview Tutorial .12 Chapter 2.

vr • The VHDL arbiter RTL source code is in the file: new_memsys/arb/rtl/arb.1 Arbiter Overview One reason Vera is flexible is because the same Vera testbench works with devices described using Verilog or VHDL. how the connections between the testbench and DUT are made. everything in Vera stays the same even though the simulator is changed. Arbiter 13 3.if. Once Vera is hooked up to the simulator. In this section. • The tutorial arbiter Vera source file solution is in this file: new_memsys/arb/test/source/arb. Arbiter This chapter focuses on the arbiter’s roll in the design. and how some of the basic signal operations behave.v • The Tutorial solution run scripts for Verilog/VHDL simulators are in the following directory: new_memsys/arb/test/run_scr • The Tutorial solution Vera interface file is in the following file: new_memsys/arb/test/include/arb. The chapter then describes the Vera methodology and functionality used to verify the arbiter section of the system.vhd • The Verilog arbiter RTL source code is in this file: new_memsys/arb/rtl/arb.Tutorial Chapter 3. It briefly describes what the arbiter does. This chapter explains how Vera interacts with both a Verilog and a VHDL design to drive signals. This chapter is divided into these sections: • Arbiter Overview • Vera Testbench Key Components • Verifying the Arbiter 3. you will be working inside the arb/test directory.vri • Tutorial solution Vera compile output files are written to the following directory: new_memsys/arb/test/vera_out . including a short timing and logic discussion.

With all the signals de-asserted. clk reset request grant xx xx 00 00 01 01 00 00 10 10 00 11 00 clk reset request grant 11 00 01 10 00 10 00 00 Figure 3-1 Arbiter Timing Diagram The arbiter implements a round-robin arbitration algorithm between two CPUs. Arbiter Tutorial • Tutorial Makefile and setup script is located in: new_memsys/arb/test Make sure you edit setup for your installation and then source the file. Once the CPU is done. the arbiter de-asserts its grant signal. . it de-asserts its request signal and. Figure 3-1 shows the arbiter timing diagram. While the grant signal is asserted for a given CPU. The arbiter queues the requests and determines which CPU will gain access to the system bus. The arbiter grants this access by asserting one of the grant output signals (grant[0] or grant[1]).14 Chapter 3. the CPU continues to assert its request signal so that both the grant and request signals for the CPU remain high while the CPU accesses the system bus. the cycle can continue with the next request. on the next subsequent clock cycle. Each CPU can drive a request input signal (request[0] or request[1]).

• Vera Shell File (filename. respectively. Typically the interface specification is contained in a file with a name of this format: filename. and handles file dumping in Verilog.test_top. • Test-top File (filename. Figure 3-2 shows the basic schematic for this configuration.Verilog and VHDL.vhd) .Defines the Vera signals for the testbench module.Tutorial Chapter 3.vri. The vrh stands for Vera compiler generated interface file or header file. Arbiter 15 3.Top level netlist file that encapsulates the DUT and the Vera testbench suite.Vera testbench • Interface Specification . Clock Generator Device Under Test Vera Shell File Input Signals Output Signals Device Under Test Vera Testbench Module Interface Specification Test-top file Figure 3-2 Test-top Configuration Schematic . and the .vhd) .v or filename_top. The Vera testbench drives the DUT through this shell file. handles the clock generation.vri suffix denotes user generated interface or header file.vrh or filename. The shell file also contains all the PLI calls in Verilog and the simulator interface calls in VHDL required to run the testbench. file that acts as a wrapper or gasket around the testbench module.vshell or filename_shell.if.2 Vera Testbench Key Components A Vera testbench suite is comprised of several key components: • Vera Testbench Module . It instantiates the DUT and the shell file.

It contains the signal and wire declarations that connect the Vera testbench to the DUT.. Arbiter Tutorial 3. Signals declared as outputs in the RTL are declared as inputs in the Vera interface (and vice versa). The signal names are taken from the top level RTL (arb.test_top.. invoke the template generator: vera -tem -t arb -c clk . Input signals are given the default skew of -1 and output signals are given the defaut skew of +1. Finally./rtl/arb.v. clock generators.v Writing vera interface file to arb. The -t switch defines the top level name of the circuit under test as arb.v). and any needed infrastructure being written in plain Verilog.test_top. It contains the Vera signal declarations made within the arb interface. Signals are driven or sampled on the positive edge of the interface clock (clk in this example).if. Writing top_file to arb.16 Chapter 3..1 How to Hook Up Verilog Vera provides a template generator to assist in the setup of this configuration. . See the Vera user guide for more information about the wizard./rtl/arb.v The generated arb.if. This could be hand generated: the instantiations and interconnections between the DUT and the Vera shell.test_top. arb. the test-top file defines a clock generator (SystemClock) that is passed to the Vera interface as the clk signal. The -c switch defines the clock signal to be used in the generated interface. The test-top file also instantiates the Vera shell file (vera_shell). Within the arbiter working directory (new_memsys/arb/test). You can customize the interface by editing this file if you want to.if.v The -tem compiler option invokes the Vera template generator.vr. you could use the interface wizard to create the interface defintion.v).vrh Writing vera template file to arb.v) is the RTL source code from which the template files are generated. arb. The declarations are made using the top level RTL (arb. Note that the names of the generated files are derived from the top-level RTL filename.2. Invoking the Vera template generator command will create the following output: Parsing . As an alternative.vrh is the Vera interface file.. Bidirectional signals remain bidirectional.vrh The generated arb./rtl/arb.v file is the Verilog test-top file. The specified file (.tmp Done.

if.vr file should look like: #include <vera_defines. See the README file in the Vera installation directory: $VERA_HOME/doc/README.vrh header file as well as the arb.if. 3.vr.tmp is the Vera template testbench file.tmp to arb.2 How to Hook Up VHDL (VCS-MX) This description shows how to hook up Vera to VCS-MX.vrh interface file.2. arb. given an interface with drives occurring on positive clock edges and a skew of 1.vrh” The arb. Bidirectional signals remain bidirectional. Within the arbiter working directory (new_memsys/arb/test) create an interface declaration.tmp The generated arb.vr. see $VERA_HOME/doc/install/install. It contains preprocessor directives that include the vera_defines. Rename the arb.if. Then create an “empty” arb.vr.vrh.pdf for the installation directions. the timing diagram is given by: clk request driven 1 time unit after driving clock edge grant driven 1 time unit after next driving clock edge request grant The Vera shell file connections to the HDL simulation are generated from the interface declarations when the Vera program file is successfully compiled.Tutorial Chapter 3. The content of the arb. Arbiter 17 Note that each interface has a clock associated with it by which all timing takes place. For this tutorial call the file arb.vrh> #include “arb. Signals declared as outputs in the RTL are declared as inputs in the Vera interface (and vice versa).vri file and the vera_defines. Input signals are given the default skew of -1 and .if.if. For example.vr. Other VHDL simulators are similar.vr file that simply includes the arb.vri and use it to define the interfaces of all the signals Vera will be connecting to in the device.simulators.vri file contains the Vera signal declarations made within the arb interface. Also. All signal operations occur on the corresponding interface clock edge. Vera provides a toplevel testbench template generator to assist in the setup of connecting Vera to the DUT.

// end of interface arb Note that each interface has a clock associated with it by which all timing takes place.if. Also.vhd file for the VCS-MX simulator. Signals are driven or sampled on the positive edge of the interface clock (clk in this example). grant PSAMPLE #-1 .vr. To create the VHDL code. you need to create the VHDL code to hook up Vera to the VHDL simulator.vr The –top compiler option invokes the Vera template generator. is also recommended that non-zero hold and setup delays are defined. Type vera -help for other VHDL simulation options including mixed language support. These signals correspond to signals in the DUT. This pulls these delays away from the clock edges and can more realistically model the back-annotated delays of the actual device. [1:0] [1:0] request PHOLD #1 . You can customize the interface by editing this file if you want to. reset PHOLD #1 .vri and the program template file arb.18 Chapter 3. type: vera -cmp -sro -top arb. . The -sro switch is used with VCS-MX for VHDL only. The -sro switch creates the appropriate top level file as well as the shell.vri” suffix to indicate a user-edited file as opposed to compiler generated. the timing diagram is given by: clk request driven 1 time unit after driving clock edge grant driven 1 time unit after next driving clock edge request grant After you have created the interface arb. Arbiter Tutorial output signals are given the defaut skew of +1.It is recommended that the customized interface file name use a “. given an inter-face with drives occurring on positive clock edges and a skew of 1. All signal operations occur on the corresponding interface clock edge.if. Your arb.vri file should look like: interface arb { input output output input } clk CLOCK . For example.

You may also refer to . Follow the directions listed in the comments at the top of arb_top.vr.vhd file is the VHDL test-top file.vhd.1 Reset Verification Verify resets are working correctly.vhd • arb_shell.vhd The arb_shell. verify the arbiter reset./rtl/arb. arb_shell.vr however. Finally. 3. Now edit the arb_top.vro The arb. The test-top file also instantiates the Vera shell file (vera_shell).vhd.vhd to give it the name of the entity and details of how the wires hook up from the Vera entity to the VHDL entity. Second. Arbiter 19 Invoking the Vera template generator creates these files: • arb_top. check for proper Arbiter handling of request sequences. With the reset signal asserted.3. .Tutorial Chapter 3.. 3.vr file instructions are used by the Vera simulator to test the DUT.vhd for more details on the VHDL DUT. verify the arbiter handles simple requests appropriately and can grant access to one of the CPUs. The declarations are made using the top level RTL (arb.vr file.vhd). otherwise the VHDL simulator could get confused about the missing Vera entity. Finally. hold the request signals inactive for each CPU (drive them to 0) and check that the grant signals are at their inactive states (0) after the reset. It contains the signal and wire declarations that connect the Vera testbench to the DUT. Be sure to call it first in the command line of the VHDL compiler before arb_top. New code can be complied into the arb.vhd is the interface from the Vera testbench to the device. First.vro is the compiled Vera testbench contained in arb. First assert the reset signal.vro arb_top. The Vera .vhd • arb. arb.vhd The arb_top.vro file with the command vera -cmp arb. the test-top file defines a clock generator (SystemClock) that is passed to the Vera interface as the clk signal.3 Verifying the Arbiter Identify the required tests. this is not necessary at this point as you have not added anything new to the arb.

Similarly. specify the interface name and the Vera signal name. Generally. the drive occurs on the next driving edge as defined in the interface (positive clock edge in our example). Arbiter Tutorial Referencing Vera Signals To reference a Vera signal. all drives occur on the positive edge of the interface clock. it advances the simulation to the next sampling edge that indicates a signal change.grant Basic Signal Operation All signal operations occur on the clock edge specified in the interface.reset arb. If the clock edge is omitted. it is best to sample signals slightly before the rising edge of the clock to avoid race conditions.20 Chapter 3. If the signal value is the same as the specified value. This advances the simulation to the next specified edge of the signal. If the delay is omitted. If an output signal is marked PHOLD. request. input signals marked PSAMPLE are sampled on the positive edge of the interface clock. the simulation terminates. For this purpose. The specified signal is compared to the given value after n clock cycles pass.request arb. Using our arb interface. and an error message is displayed (note that the error mode can be set so that errors do not terminate the simulation using the soft keyword). a verification error occurs. and grant signals are referenced as: arb. To advance the simulation to the next change of a specified signal. use the synchronize construct: @(clock_edge signal_name). If there is a mismatch. To assert and de-assert the signals. The soft keyword should be used in conjunction with the flag() method to determine if the expect was satisfied.Below the already included #define statements add: #define INPUT_SKEW #-1 This define should be set in arb . define an input skew of -1 unit inside the arb. The specified signal is driven to the appropriate value after n clock cycles pass.vr.vr following the other defines generated by the template generator or by the user. use the Vera expect construct: @n signal_name == value.tmp file. the reset. the simulation continues. To check that a signal has a specific value at a specified time. use the Vera drive construct: @n signal_name = value. .

Runtime version 7. add this code to the arb.test_top.reset = 1.vr.vr. Also see the README file description containing the simulation output. //assert reset @1 arb.vr Compiling the testbench generates the Vera shell file (arb. Run the simulation: vcs arb..grant==2’b00. not your custom code.vro Your test should run to completion without any errors and the output should be as follows: Compiler version 7.tmp file to verify the resets: arb. Compile the Vera testbench: vera -cmp arb. // de-assert reset after 1 clock cycle @0 arb. Vera: finish encountered at time total mismatch: 0 vca_error: 0 fail(expected): 0 drive: 3 expect: 1 sample: 0 sync: 0 250 cycle Aug 6 17:18 2003 3 .vro). First.0.vshell -vera simv +vera_load=arb. To compile and run your code you must follow the steps outlined below: With the code added to the arbiter testbench (arb.tmp to arb. run the simulation and test the results.vshell) and the Vera testbench binary object file (arb. NOTE: The prebuilt scripts will run the solution.request = 2’b00. If you want to use any of these to run the tutorial solution. invoke the script from the test directory.0.reset = 0.1. Running the Simulation with VCS At this point you should be in the arb/test directory.vr.Tutorial Chapter 3.1./rtl/arb. // de-assert request on next positive clock edge @1 arb.v arb.tmp). verfiy that you have renamed arb. See the “run_scr” directory for different simulator run scripts and the makefiles for both Verilog and VHDL.v .vr. Arbiter 21 Verifying the Reset Using the basic signal operations described earlier. // check that grant is de-assert after 1 clock cycle Note that the request and grant signals are 2-bit signals. Each bit of the signals must be de-asserted.

cycle 3) $stop at time 250 Scope: arb_test_top.setup "DEFAULT : work" >> .1. run the simulation and test the results.vr). See the “run_scr” directory for different simulator run scripts and the makefiles for both Verilog and VHDL.vr. 1) Create .vr Compiling the testbench generates the Vera testbench binary object file (arb. change the grant de-assertion line within the arb. the simulation terminates and an error message is reported. Note – Remember to edit the testbench file to correct this error before continuing.grant.0.1. These files need to be included when the simulation is run.grant==2’b01. Runtime version 7.vro).synopsys_vss.synopsys_vss.22 Chapter 3. This results in an expect mismatch and a verification error as shown below. EXPECT MISMATCH TIME: 250 CYCLE: 3 Signal: arb. With the code added to the arbiter testbench (arb.) In this case. (The HDL does not need to be recompiled when only the Vera code is changed. Arbiter Tutorial If there are verification errors when the simulation is run. Recompile the Vera code and run the simulation again. If you want to use any of these to run the tutorial solution. For instance.vshell File: arb.vr file so that it is incorrect: @1 arb. Also see the README file description containing the simulation output.0 Exp Value: 1 : 01 Actual Value: 0 : 00 VERIFICATION ERROR: Expect mismatch arb_test (arb. line 14.synopsys_vss. the testbench expects that the grant signal is asserted while the Verilog model continues to de-assert the signal as before. You will observe the following output: Compiler version 7.setup >> .vshell Line: 50 Location: WAIT_ON_EXPECT in program Aug 6 17:21 2003 Running the Simulation with VCS-MX At this point you should be in the arb/test directory.synopsys_vss.setup file echo echo echo "WORK > DEFAULT" > "TIMEBASE = ns" . invoke the script from the test directory. Compile the Vera testbench: vera -cmp arb.setup .0.

drive bit 0 of the request signal and then monitor bit 0 of the grant signal.request = 2’ >> .vhd vhdlan -nc -event ./vera_out/sc./vera_out/sc.vro" echo "vera_continue_on_error ON" > . Arbiter 23 2) Create vera. Finally. Test For Simple Request by CPU0 To test that simple requests are handled correctly for 3. @0 arb. // de-assert bit 0 of request @2 arb.3./vera_out/sc./vera_out/sc. monitor the request signals.ini 3) Analyze vhdl source code vhdlan -nc -event .vhd vhdlan -nc -event ./vera_out/arb_shell../vera. de-assert both bits of the request signal and check that both signals of the grant signal are properly de-asserted. Finally. // assert bit 0 of request @2 arb./vera. drive bit 1 of the request signal and then monitor bit 1 of the grant signal.ini >> ./vera_out/sc. // check that bit 0 of grant is asserted @0 arb. check that the grant signal is set appropriately.grant == 2’ >> . // check that both bits of grant are de-asserted Test For Simple Request by CPU1 To test that simple requests are handled correctly for CPU1.Tutorial Chapter 3./vera_out/ >> . de-assert both bits of the request signal and check that both signals of the grant signal are properly de-asserted.2 Simple Request Verification (Verilog and VHDL) To check if the arbiter is handling simple requests correctly.vhd 4) Create scsim (compile) scs -nc DUT_BENCH_CFG 5) Create run script (optional) echo "# echo "# " "> .grant == 2’b00./rtl/arb.request = 2’b00. .ini file with inclusion of arbiter object code echo "vera_load=./vera_out/arb_top. and then check that the grant signal is de-asserted after the request is echo "#Type ’run’ to start simulation" echo "run " 6) Run simulation scsim -nc -include .

the now unexpected signal change generates an error. // check that other grant is asserted @1 arb.grant). Arbiter Tutorial @0 arb. there is no way to ensure that grant does not change unpredictably (it is only checked using the expects).grant == 2’b10. // check that both bits of grant are de-asserted 3.grant == 2’b00. // check that both grant signals are de-asserted Given this testing configuration. When the VCA is turned on.grant == 2’b00. This signal declaration enables the VCA for the grant signal. // de-assert bit 0 of request @2 arb.request = 2’b00. To check for unexpected changes. The VCA generates a verification error when unexpected changes occur.vri: input [1:0] grant INPUT_EDGE INPUT_SKEW vca r0. // check that grant de-asserts for 1 cycle @1 arb. the code to check arbiter behavior is: @0 arb.if. // assert bit 1 of request @2 arb.3 • • • • • • Sequenced Request Verification Verify sequences of requests are handled properly by checking a series of conditions: Assert both request signals and check for correct grant assertion Release the granted request and check for grant release Assert both request signals and check for correct grant assertion Release the newly granted request and check for grant release Check for new grant assertion Release last request and check that both grants are released Given this verification methodology. assigning a default quiescent value of 0 to the signal.grant == 2’b10. // de-assert corresponding request @2 arb.3.request = 2’b11.grant == 2’b01.grant == 2’b01. Comment out one of the expect statements and run the simulation. This has already been included in the .request = 2’b10. To use the VCA. turn it on from within the testbench (before the test sequence begins): vca(ON. // assert both request signals @2 arb. // check for first grant @0 arb. // de-assert corresponding request @1 arb.request = 2’b01. // de-assert both request signals @2 arb. the signal declaration in the interface file for the signal being monitored must include the vca keyword as shown.grant ==2’b00. To enable the VCA. // check that grant de-asserts for 1 cycle @1 arb. any change in signal grant that is not expected (by an expect statement) or explicitly driven generates a verification error. .request = 2’b10. use Vera’s Value Change Alert (VCA). // check for first grant @1 arb.request = 2’b11. arb. // check that bit 1 of grant is asserted @0 arb.24 Chapter 3. // assert both request signals @1 arb.request = 2’b00.grant == 2’b00./include/arb.

These concepts are presented within the verification framework so that you can learn how to adequately validate our memory controller. Memory Controller This chapter discusses the memory controller portion of the design.1 Memory Controller Overview In our system. including a description of virtual ports and binds as well as synchronous and asynchronous events. All edits will be performed inside the new_memsys/cntrlr/test directory. it puts its request on the system bus. Memory Controller 25 4. The memory controller acts on this request by reading data from the SRAM devices and returning data when necessary. It discusses some of the major features of Vera that are used to verify the controller.v • The project Makefile is located in : new_memsys/cntrlr/test type ‘make help’ for details • The Tutorial solution Vera interface declaration is in the following program file: new_memsys/cntrlr/test/source/cntrlr.Tutorial Chapter 4. Once the CPU has access.vr • The VHDL controller RTL source code is in the file: new_memsys/cntrlr/rtl/cntrlr.vr • The Tutorial solution Vera compile output files are written to the following directory: new_memsys/cntrlr/test/vera_out . • The tutorial controller Vera source file solution is in this file: new_memsys/cntrlr/source/cntrlr. This chapter includes these sections: • Memory Controller Overview • Verifying the Memory Controller • Using the Vera Debugger with the cntrlr Example 4. It gives an overview of how the memory controller functions.vhd • The Verilog controller RTL source code is in this file: new_memsys/cntrlr/rtl/cntrlr. the CPU accesses the bus through the arbiter.

allocating a maximum of 64 bytes of memory to each. Figure 4-1 shows a diagram of how Vera works with both the system bus and SRAM device signals. Vera rdWr_N ramAddr ce_N ramData SRAM Side Figure 4-1 MEMORY CONTROLLER adxStrb busAddr busRdWr_N busData System Bus side Vera/Memory Controller Interaction . The address bus is 8 bits wide. Memory Controller Tutorial • Tutorial Makefile and setup script is located in: new_memsys/cntrlr/test Make sure you edit setup for your installation and then source the file. The memory controller reads requests from the system bus and generates control signals for the SRAM devices attached to it. The controller supports up to 4 devices. For read requests.26 Chapter 4. the controller reads data and transfers it back to the bus and the CPU making the request. which creates an address space of 256 bytes. The controller decodes the address and generates the chip enable for the corresponding device during a transaction.

Memory Controller 27 Figure 4-2 and Figure 4-3 show the timing diagrams for the memory controller’s read and write operations respectively (note the signal names as you will be using them in the verification process) clk reset adxStrb busAddr busData busRdWr_ cex_ valid valid ramData ramAddr rdWr_ Figure 4-2 valid valid Memory Controller Read Operation Timing Diagram .Tutorial Chapter 4.

model the behavior of the 4 different memory devices in Vera. create Vera tasks that drive the bus for read and write operations.vhd. Note that this chapter checks the memory controller by emulating both the system bus and the memory bus behavior. To start the verification for Verilog designs. To do this.2 Verifying the Memory Controller To completely check the functionality of the memory controller.v To start the verification with VHDL designs. First. Memory Controller Tutorial clk reset adxStrb busAddr busData busRdWr_ cex_ valid valid ramData ramAddr rdWr_ valid valid Figure 4-3 Memory Controller Write Operation Timing Diagram 4.28 Chapter 4. Then check the integrity of the read and write operations. create the template files using the -tem switch as described with the arbiter verification: vera -tem -t cntrlr -c clk . Finally. follow the steps in the previous chapter for arb.Rather than connecting the rtl models of the memory to the controller. You are provided an example of the Vera interface definition inside the controller testbench program file .vr. perform a series of tests. check the read and write capabilities of the controller./cntrlr/source/cntrlr. . but now working with cntrlr../rtl/cntrlr. exhaustively check the address map (all 256 addresses) for the read and write functions.

cntrlr. cntrlr. @1 cntrlr. this check is made using multiple threads.adxStrb = 1’b1. The task should use an 8-bit bus address as an input. } . cntrlr. Note: do not drive the data onto the bus and check for the expected data here.busRdWr_ = 1’b0. It then drives the busAddr signal to that value.2.busAddr = adx. Finally.busRdWr_ = 1’b1. Given these requirements. cntrlr. create two Vera tasks that drive the bus for read and write operations. Given this requirement. cntrlr. bit[7:0] data) { @1 cntrlr.adxStrb = 1’b1. Finally. Read Operation Create a task that drives the read operation onto the system bus as specified in the timing diagram for the controller.busData = 8’bzzzzzzzz. Memory Controller 29 4.1 Driving the System Bus For Read and Write Operations In testing the read and write capabilities of the controller. the write operation task is: task writeOp (bit[7:0] adx.busRdWr_ = 1’b1. cntrlr.adxStrb = 1’b0.Tutorial Chapter 4. @1 cntrlr.adxStrb = 1’b0. Write Operation Create a task that drives the write operation onto the system bus as specified in the timing diagram for the controller. When checking the entire system in Chapter 5 ”Memory System”. the task should leave the bus in an idle state (defined when busData is in high z and busRdWr_ is de-asserted).busData = data. } This task is passed the argument adx. The task should use 8-bit address and data busses as inputs. the read operation task is: task readOp (bit[7:0] adx) { cntrlr. Before checking for the expected data.busAddr = adx. check that the read operation displays the correct waveform at the SRAM interface. it drives the busRdWr_ and adxStrb signals such that they match the timing diagram for the read operation of the controller. cntrlr.

it drives the busData.The port_variable is the name of the variable being declared. Outside the main program block. . } port_name . So in essence. port_signal_memberN.2. busRdWr_. all of the signals in the port are bound. port_signal_memberN . 4. The only limit is the limit of the verification engineer’s imagination. It then drives the busAddr signal to that value. use the bind construct: bind port_name port_variable { port_signal_memberN interface_name.signal_name. These signals can be passed to tasks that you want to act on specific sets of signals.The port_name must be a valid identifier. Binding Virtual Port Signal Members to Interface Signals The bind construct (For a complete discussion see “bind Construct for Static Connection. . This feature allows a task to be written once.30 Chapter 4. Memory Controller Tutorial This task is passed the argument adx. but also involves declaring a port variable as well.. you can bind selected signals if you want.” in the Vera User Guide) not only associates port-signal-members with interface signals. Defining Virtual Ports Vera virtual ports are defined outside the main program block using this construct: port port_name {port_signal_member1.. then re-used many times at different interfaces to the design under test. port_variable . port_signal_member . Vera turns interface connections into parameters that can be passed around the testbench as needed.). The port variable allows task and function reuse by giving the verification engineer the ability to pass task or function specific interface connections to both tasks and functions. The virtual ports are then bound to specific interface signals as needed.port_signal_memberN must be a valid identifier.. which is a set of generic port signal names that act as placeholders for the actual interface signals they are bound to.} port_name .The port_signal_memberN is the name of the generic signal names you are including in the bind. and leave others unbound. Multiple port signal names are separated by semi-colons (.The port_name is the user defined virtual port whose signal member names you want associated with interface signals.2 Implementing Virtual Ports Vera’s virtual ports allows the grouping of Vera interface signals into logical bundles. Finally. This is done by defining a virtual (or generic) port. and adxStrb signals such that they match the timing diagram for the write operation of the controller. However. Generally.

define a device port for the SRAM parts (ramAddr.ramData.The port_name is the name of the port data type. use this construct: $signal_name This references the specified port signal in the bind passed to the subroutine. To reference individual port signals within a subroutine. ramData cntrlr. Referencing Ports and Binds To reference or pass a port to a subroutine. rdWr_. use port variables.Tutorial Chapter 4. . is: port_name port_variable = initial_value. The syntax to declare a port variable.The port_variable is the name of the port variable you are declaring. when not using the bind construct. Each virtual port definition becomes a new data type (much like enumerated types) that can be used to declare new port variables. connect the port signals to actual interface signals using the bind construct: bind device device0 { ramAddr cntrlr. Memory Controller 31 interface_name . signal_name . ce_. port_name . ramData. the port_variable has a NULL value until it is assigned a port.The initial_value can be any existing port of the same type as the port variable.The signal_name is the name of the signal you are binding to a particular port signal member. You can specify signal subfields using signal_name[x:y]. ramData. rdWr_.The interface_name is the name of the interface to which you are binding the port signal members. If it is not set. } After defining the virtual port. port_variable . initial_value . Implementing Ports and Binds in the Memory Controller Given the port/bind methodology presented here. and ce_): port device { ramAddr. Port variables store virtual port/bind pairs.ramAddr.

Because of complex timing issues with the read operation. This mechanism provides a means to evaluate a signal over a specified period of time. d. device2. It connects the port signals to their corresponding interface signals. the check is made immediately and lasts y cycles after the call. The task should have an argument of port variable type device so that we can pass in the signals we want it to act on.rdWr_N.$ramData == data. the code is: task checkSramWrite (device d. } This bind construct results in the port variable device0 of port type device. The signal value must match the expected value for the duration of the check. Similar binds for each device (device1. . Verifying the Write Operation To verify the write operation. The task also has 6-bit address and 8bit data busses as inputs. Our approach now is to make use of Vera’s timing windows. The window of time for which the check is made must be in the form x.5 d. The syntax is: @window signal_name == value. and drive the data onto the ramData bus at the appropriate time. Timing Windows Vera provides timing windows for its expect signal operation.$ramAddr == adx.3 Verifying Read and Write Operations The memory controller issues read and write operations to each of the four SRAM devices as shown in the earlier timing diagram. For more details. Memory Controller Tutorial rdWr_ cntrlr.32 Chapter 4. The check begins x cycles after the call is made and continues for y cycles after the call is made.ce0_N. which allow you to specify ranges of time and event sequences. examine the write operation first. we modeled the timing diagram exactly. create a Vera task that checks the SRAM write operation against the timing diagram provided. Note that the ce_ signal is connected to its device-specific signal.$ce_ == 0.y. Earlier. cycle by cycle. A discussion of the timing issues and the read operation follows. It must check that the SRAM signals are driven correctly. Create read and write tasks in our testbench that check these operations. @. bit[7:0] data) { @1. bit[5:0] adx.$ramData == data. and device3) should be constructed. d.$rdWr_ == 0. ce_ cntrlr. 4. @1 d.2 d.2. Given these requirements. If the x is omitted (.y). see the Vera User Guide. check that the address is the right address.

and the input data is driven as return data. d. all Vera signal operations can be used asynchronously by adding the async keyword after the operation: @(edge signal_name async). This must happen in the same clock cycle for the device . Synchronous and Asynchronous Timing By default.Tutorial Chapter[7:4] == 4’b0101 async. data[2:0] = main_bus.$ramAddr == adx. Next check that rdWr_ and ce_ are de-asserted. // drive new value immediately signal_name == value[3:0] = 4’b1010 async. @1 d.$ramData == data.$ramAddr == adx. However. and check that the address and write data are still valid. Verifying the Read Operation To verify the read operation. memsys. The SRAM device drives data after the corresponding ce_ signal is asserted. That is they occur on the clock edges specified in the interface specification.$rdWr_ == 1. d. After the checks. These are examples of async statements: @(posedge main_bus. an interesting timing issue arises in this case.// execute expect expression immediately Note that the delays for the drive and expect operations are not used since they occur immediately. all Vera signal operations are synchronous. @1 d. Memory Controller 33 d.[2:0] async. check that rdWr_ and ce_ are asserted simultaneously for exactly one cycle. } This task checks that the address (ramAddr) is valid over the timing window 1-5 cycles after the call is made.request async). //advance to next edge of signal signal_name = value async. After checking these signals. main_bus. check that the control signals are asserted. the correct address is driven by the memory controller. and check that the address and write data remain valid. make sure ramData returns to tri-state.$ce_ == 1.$ramData == 8’bzzzzzzzz. d. The the write data (ramData) is checked for two cycles from that point.

This is the code: task checkSramRead (device d. create a read task that checks the read operation against the timing diagram provided. drive the data back to tri-state at the next rising clock edge (note the use of the <= drive operator. d. This means that the data is driven on the next rising clock edge. Memory Controller Tutorial to work.$ce_ == 0 async. immediately check that ce_ is 0. d. Finally. . bit[7:0] data) { @1.$ramData = data async. because of the sampling skew. Next advance the simulation to the exact change of the chip enable signal (ce_) using the synchronize construct.5 d.$ramData <= 8’bzzzzzzzz. and ramAddr has the appropriate value. This timing diagram shows this behavior: clk ce_ ce_ is driven just after rising edge data should be driven here data VALID data is driven here because of sampling skews With this in mind. which is invalid.34 Chapter 4. @(d. d.$rdWr_ == 1 async. rdWr_ is de-asserted. Next. Use the async form because we want this change to happen immediately without waiting for the next sampling edge. which indicates a non-blocking drive so that execution continues immediately). However. } This task first checks that the address is valid over the specified window of time. @1 d.$ramAddr == adx async. It also has 6-bit address and 8-bit data busses as inputs. The task must have an argument of type device to pass in the virtual port. d.$ce_ async). bit[5:0] adx. ce_ is sampled just after the rising clock edge.$ramAddr == adx. Use the async construct here so that the drive is done immediately after the checks and not on the next rising clock edge. After these checks. drive the data (ramData) immediately.

The code to drive the bus for the write operation is in the writeOp task. if you change the address parameter to a value that is not valid for the device you are checking. use these two functions: writeOp (8’h01. 6’b000011.ce0_N == 1’b1.Tutorial Chapter 4. This fails because of the address dependence in activating the chip enable signals within the RTL.ce1_N == 1’b1. assert the reset signal and de-assert adxStrb. To check the controller reset. Now add in the code to check the write operations for the other devices. 6’b000001. @1. @1 cntrlr.ce2_N == 1’b1. To test this behavior.adxStrb = 1’b0. These requirements are met with this code: cntrlr. cntrlr. cntrlr. The same tasks can be used with different virtual ports and different address parameters. With the reset check completed. remember to recompile after making the changes. 8’h95). When checking other devices.busData == 8’h95. write code to check the write operation of one of the devices. Remember to check that the returned data matches the return data specified in the timing diagram. the check fails.ce3_N == 1’b1. 8’h5A). cntrlr. The code for these checks is: readOp (8’h03). and the code to check that write operation is in the checkSramWrite task. Memory Controller 35 Running the Simulation Before running the simulation. de-assert the reset signal. Finally. . Next check that all the chip enables are de-asserted (cex_). 8’h5A). set up a reset check to ensure that the controller is resetting correctly. checkSramRead (device0. @1 cntrlr.reset = 1’b0.100 cntrlr.reset = 1’b1. Similarly. cntrlr. remember that each device has a range of valid addresses: Device 0 1 2 3 Valid Address Range 0-63 64-127 128-191 192-255 Because the address busses are device specific. checkSramWrite (device0. To check the operation. use the generic tasks to drive the bus for read operations and check the device read operations. This code drives the bus and then checks the write operation using the specified virtual port’s signals (device0).

checkSramRead (dev. Memory Controller Tutorial This code drives the bus and then checks the read operation using the specified virtual port’s signals (device0). Add Value Change Alert (VCA) checks to the interface specification to enable the VCA for each chip enable signal.i++) { index = i. } Each iteration of this for loop acts on a different address. Now create a for loop. We have exhaustively tested the address space. 8’h5A). The bus data is checked at the end of each iteration to monitor the return values. readOp(index). 2’b11: dev = device3. integer i. } checkSramWrite (dev. using a case statement to switch device ports and calling our subroutines to drive the bus and check the SRAM operations: bit[7:0] index. The case statement changes the virtual port on which the subroutines act so that they use the correct signal bundles for each device. 8’h5A). the return data is checked to see that it matches the correct value. First... This defines a variable of port type device is used to pass in the ports to each subroutine call in our loop. Finally. . 2’b01: dev = device1. To exhaustively test the entire range using these calls.36 Chapter 4. use virtual ports and for-loops. for (i=0. Remember the VCA must be enabled for each chip enable signal by adding the vca r1 keywords to each signal declaration in the interface. each task must be called with every address. 2’b10: dev = device2. . writeOp(index. index[5:0]. index[5:0].i<=255. It drives the bus operation and then checks the SRAM operation using the subroutines defined previously. 8’h5A). define a port variable that will have each device’s port signals assigned to it through the loop: device dev. @1 cntrlr. These tests only cover a subset of the valid addresses. To simplify this task.busData == 8’h5A. case (index[7:6]) { 2’b00: dev = device0. but must also make sure that the chip enables (cex_) do not change unexpectedly through the test.

ce3_N). disable the VCAs when the checkSramRead task is executed and enable them once it is completed. vca(ON. cntrlr. Do this by adding in case statements to the above block: readOp(index). vca(ON. cntrlr. Memory Controller 37 Now turn on the VCAs before the reset check using this code: vca(ON.ce1_N). cntrlr.ce1_N). disable the VCA for the device we are checking. vca(ON.ce0_N).ce3_N). 2’b01: vca(OFF. +vera_debug_on_error causes the debugger to come up in the event of a verification error. cntrlr.ce3_N). cntrlr.ce0_N).3 Using the Vera Debugger with the cntrlr Example Refer to the Debugger tutorial by invoking “vera-doc” and selecting the debugger tutorial under application notes. 8’h5A). the VCA is immediately enabled. cntrlr. cntrlr.ce2_N). So. index[5:0]. After the check is made. } checkSramRead (dev.vr” contains a commented breakpoint command.Tutorial Chapter 4. cntrlr. cntrlr. 4.ce2_N). case (index[7:6]) { 2’b00: vca(ON. cntrlr. 2’b11: vca(ON. 2’b10: vca(ON. } After driving the bus with the readOp task. cntrlr. 2’b10: vca(OFF.ce1_N). The cntrlr module test code contained in “source/cntrlr. This is because of our asynchronous sampling of cex_ in the checkSramRead task. Uncomment it and recompile and run the simulation to bring up the debugger. Using either or both of the following runtime options will bring up the debugger: +vera_debug_on_start brings up the debugger immediatley before running the simulation.ce2_N). . The Vera “breakpoint” command can be used inside the code to start the debugger. case (index[7:6]) { 2’b00: vca(OFF. 2’b11: vca(OFF.ce0_N). 2’b01: vca(ON. cntrlr. Note that running the simulation with the VCAs enabled like this fails.

Memory Controller Tutorial .38 Chapter 4.

runtime signal mapping. and SRAM devices. functional coverage.vri . This chapter includes these sections: • Memory System Overview • Verifying the Memory System 5. Memory System 39 5. object-oriented programming. this chapter uses these features to validate our memory system. Finally.vr • The VHDL memsys RTL is in the file: new_memsys/memsys/rtl/memsys.ports_binds.Tutorial Chapter 5.1 Memory System Overview You will be working inside the new_memsys/memsys/test directory.v • Tutorial solution run scripts for Verilog/VHDL simulators are in the following directory: new_memsys/memsys/test/run_scr • The Tutorial solution Vera interface file is in the following file: new_memsys/memsys/test/include/memsys. triggers. and mailboxes.vri • The Tutorial solution Vera ports and binds file is in the following file: new_memsys/memsys/test/include/memsys. It also discusses some of the higher level verification techniques used in Vera.if. • The tutorial memsys Vera source file solution is in this file: new_memsys/memsys/source/memsys. which includes the arbiter. and random stimulus generation.vhd • The VHDL memsys RTL toplevel netlist is in the file: new_memsys/memsys/rtl/memsys3_oop_top. These include concurrency control mechanisms such as regions. This chapter briefly overviews the system.vhd • The Verilog memsys RTL netlist is in this file: new_memsys/memsys/rtl/memsys. Memory System After discussing the arbiter and memory controller separately. controller. we now examine the way the components act in a complete system.

2 Verifying the Memory System The methodology used to verify the entire memory system is broken down by tasks and concepts: Confidential and Proprietary Synopsys Inc.40 Chapter 5. with access granted through the arbiter. A schematic of the complete system is given in Figure 5-1. memory controller. the system bus is driven by two separate CPUs. and four SRAM devices. S R A M S R A M S R A M S R A M ce0_N rdWr_N ce1_N reset MEMORY CONTROLLER ROUND-ROBIN ARBITER ce2_N request[0] CPU0 grant[0] System Bus ce3_N address data Figure 5-1 Memory System Schematic grant[1] CPU1 request[1] 5. Memory System Tutorial • The Tutorial solution Vera compile output files are written to the following directory: new_memsys/memsys/test/vera_out • Tutorial Makefile and setup script is located in: new_memsys/memsys/test Make sure you edit setup for your installation and then source the file. In our system. . The memory controller handles the reading and writing of data to and from the system bus. The memory system acts as a wrapper that instantiates the arbiter.

OOP allows us to simplify our testbench and provide re-usable code blocks.busRdWr_ = 1’b1. memsys. @1. checks that each address is unique before the bus is requested. } .busData = data.Tutorial Chapter 5. memsys.signal).request = 2’b00.adxStrb = 1’b0. deassert the request signal. memsys. • Functional Coverage .busData = b’bzzzzzzzz.Reset verification and read/write operations. 5. we must assert the reset signal. memsys. Reset Verification To check that the system is resetting correctly. Mailboxes allow us to use random addresses while checking that each address is used only once.3 memsys.grant == 2’b00.Vera’s coverage objects help ensure that our address space is tested sufficiently. and then deassert the reset signal. memsys. memsys.adxStrb = 1’b0.Using regions.Using triggers. we develop a testbench that checks both CPUs running concurrently using multiple threads.reset 1’b1. Finally. bit[7:0] data) { @1 memsys. • Basic Concurrency Control . We can use the write operation task by simply modifying the interface names in the signal operations (memsys.busAddr = adx. The code to check the reset is: memsys. memsys.2.reset = 1’b0. • Object Oriented Programming (OOP) . check that the grant signal releases properly. @1 memsys. release the adxStrb signal. Memory System 41 • General Verification . memsys. The writeOp task is: task writeOp (bit[7:0] adx.busRdWr_ = 1’b0.adxStrb = 1’b1. • Interprocess Communication . we advance or simulation in lock-step fashion to make sure that data is read only after it is written to the bus. Read and Write Operations The bus in the system is very similar to the bus used in the memory controller.1 General Verification The general verification tasks include checking the reset procedure and modifying the read and write operations previously developed for the memory controller.

If it blocks. bit[7:0] data) { @1 memsys.5 memsys. code after the fork/join block executes immediately.busRdWr_ = 1’b1. our read operation task must check for the correct return data. we want to set up our testbench so that each CPU issues a series of reads and write requests to the memory system with random addresses and data. .busAddr = adx. The wait_option must be one of the following: all any none The all option is the default.busData == data. The syntax to declare a fork/join block is: fork {statement1.. memsys.} {statement2. Threads within the fork/join block are scheduled but not executed until the code following the fork/join block hits a blocking statement. When the none option is used.} join wait_option statementN .adxStrb = 1’b0.The wait_option specifies when the code after the fork/join block executes. the code below the fork/join block will not execute until the code inside the fork/join thread returns. without waiting for any of the fork/join processes to start. @2.adxStrb = 1’b1.42 Chapter 5. The new readOp task with the data checking included is: task readOp (bit[7:0] adx. When the any option is used. code after the fork/join block executes after any single concurrent process within the fork/join is completed. Code after the fork/join block executes after all of the concurrent processes have completed.. With the read and write operations defined. The fork/join block can be either blocking or non-blocking. @1 memsys.} {.The statements can be any valid Vera statement or sequence of statements. The CPUs should then request and Confidential and Proprietary Synopsys Inc.} {statementN. Memory System Tutorial In addition to driving the read operation onto the system bus. Each CPU should use the random() system function to generate random addresses within the valid address space and an 8-bit data type. } Multiple Threads Fork/join blocks are the primary mechanism for creating concurrent processes. wait_option . memsys.

// get 32 bit random variable address0 = randVar0[13:6].grant == 2’b01. // issue read operation @1 memsys. // release request @2. // request again @2. // check for grant readOp(address0.20 memsys. // issue write operation @1 memsys.grant == 2’b00. // request again @2.20 memsys.request[1] = 1’b0. data1). // issue read operation @1 memsys.grant == 2’b00. // get random 8-bit address data0 = randVar0[29:22].20 memsys.grant == 2’b00.20 memsys.request[0] = 1’b0.20 memsys. // check for release @1 memsys. This sequence should be repeated 256 times using the repeat() flow control statement. data0). the code is: random(12933). // request the bus @2.request[1] = 1’b0. data0). // request the bus @2. // check for grant writeOp(address1.request[0] = 1’b1.grant == 2’b00.20 memsys. // check for grant readOp(address1. // release request @2. // get 32 bit random variable address1 = randVar1[13:6]. // get random 8-bit address data1 = randVar1[29:22].20 memsys. // check for grant writeOp(address0.Tutorial Chapter 5. and release the bus (check for the release of the grant signal upon bus release).request[1] = 1’b1.grant == 2’b01. write the data to the bus. // get random 8-bit data @1 memsys. // issue write operation @1 memsys.request[0] = 1’b1. // release request @2. // get random 8-bit data @1 memsys. Given these criteria. // release request @2. fork { // CPU0 repeat(256) { randVar0 = random().grant == 2’b10. data1).20 memsys. Memory System 43 access the bus.grant == 2’b10.request[0] = 1’b0. // check for grant } } join // call random with seed . // check for release @1 memsys.request[1] = 1’b1. // check for grant } } { // CPU1 repeat(256) { randVar1 = random().

the data that CPU0 reads is different than expected (it reads the data that CPU1 wrote). you must use the alloc() system function: function int alloc(REGION. A solution to this issue is to use basic concurrency control and is discussed in the next section. int region_id.2 Basic Concurrency Control In our system. TIN can be created.The region_count specifies how many regions you want to create. valueN . without X’s or Z’s. it returns 0. The region_enter() system function checks to see if a particular region is in use: function int region_enter(keyword wait_option. For instance. To allocate a region. Conceptually. int region_count). wait_option . we must check that the address is unique before the bus is requested to avoid conflicts. To do this. The NO_WAIT option continues code execution if the specified region is in use. These letters are the only letters from which words can be made. Otherwise. regions can be viewed as a set of letters. Effectively.44 Chapter 5. These values specify the unique region values. This results in simulation failure because of the discrepancy between data read and expected data. we use regions.. You should generally use 0.The region_id is the ID number of the particular region being created. bit|int value1. and CPU1 then writes to the same address space. region_id . no one else can spell TIN because the T is already in use. region_count . Memory System Tutorial This test works well in exhaustively checking the read and write operations for each CPU. valueN).The wait_option can be either NO_WAIT or WAIT.The region_id specifies which region is being entered. because both CPUs are accessing a single bus. Confidential and Proprietary Synopsys Inc. If one person uses the letters to spell CAT. value2.The values are integer or bit vectors up to 64 bits. problems arise when each CPU accesses the same address space with different data. First you allocate which letters are included in the set. Once the T is returned. this ensures that data sets are unique.2.. Vera automatically generates a region ID.. and it eliminates concurrent crossover. When you use 0. It must be an integer value. region_id . Region Overview A Vera region is a mutual exclusion mechanism that guarantees that the requested values are unique in the simulation. 5. . The WAIT option suspends the process until the specified region is no longer in use. if CPU0 writes to an address space. The alloc() function returns the base region ID if the regions are successfully created. int region_id. It must be an integer value. . However.

Any processes that are suspended (waiting for region values) execute when the region values are made available. trace(ON.20 memsys. value2.. the function suspends the current thread until the values become available. fork { // CPU0 repeat(256) { randVar0 = random(). Finally. // get random 8-bit address data0 = randVar0[29:22]. The syntax is: task region_exit(int region_id. regId).The region_id specifies which region that is being exited. // release request // call random with seed . regId. we must allocate the region before the forked process. REGION. and the current region cannot use them. without X’s or Z’s. The region_exit() system task removes the specified values from the in-use state. Implementing Regions To implement regions within the testing framework established in the previous section. .Tutorial Chapter 5.grant == 2’b01. Memory System 45 The region_enter() system function checks the specified values against all region values for the specified region. If one or more of the values is in use elsewhere.. These requirements are satisfied using this code: regId = alloc(REGION. we should force each CPU to wait a random number of cycles after the sequence is executed before running the sequence again. data0). the function returns a 1. the address is removed from the pool of valid addresses and the region prevents the other CPU from using the same address until the region is exited and the value returned. Next. // issue write operation @1 memsys. within each CPU fork. If none of the values are in use elsewhere. Each fork must include a region enter and a region exit to accomplish this. Then. These values specify the unique region values. If another process has entered the region with one or more of the values. depending on the wait option.log file after the simulation. valueN). and passes control to the next line of code. // check if address is free @1 memsys.The values are integer or bit vectors up to 64 bits. the CPU enters the region with its address value.request[0] = 1’b1. 0. random(12933). region_id . You can monitor the region to see how the synchronization works using the trace() system function and checking the verilog. bit|int value1. // request the bus @2.. 1). // check for grant writeOp(address0. address0). valueN . then those values are in use.request[0] = 1’b0. When the region_exit() system task is called. flags the values as in use. the specified values are no longer in use and can be used in other regions. // get random 8-bit data region_enter(WAIT. // get 32 bit random variable address0 = randVar0[13:6].

// check if address is free @1 memsys. // request again @2. // release request @2. data1). and how random stimuli are built into the Vera objects. // check for grant writeOp(address1.grant == 2’b10.request[1] = 1’b0.20 memsys. Memory System Tutorial @2.grant == 2’b00. we examine how classes can be implemented into our memory system using modified port declarations.grant == 2’b01.2.request[1] = 1’b0.request[0] = 1’b0. // exit region repeat (randVar0[20:17]) @(posedge memsys.20 memsys. } } { // CPU1 repeat(256) { randVar1 = random(). In this section.grant == 2’b00. // check for grant readOp(address0. // release request @2. // request again @2. // request the bus @2. regId. // get 32 bit random variable address1 = randVar1[13:6]. // get random 8-bit data region_enter(WAIT. // check for release @1 memsys. address1). // check for grant region_exit(regId.grant == 2’b00.20 memsys.request[1] = 1’b1.grant == 2’b10. // exit region repeat (randVar1[20:17]) @(posedge memsys. Confidential and Proprietary Synopsys Inc.request[0] = 1’b1. how classes are constructed. data0). // issue write operation @1 memsys.3 Object Oriented Programming (OOP) OOP allows you to develop programs that are easier to debug and easier to reuse by encapsulating related code and data together and making access to the class formal and rigorous. Vera then uses many of its features within this object-oriented framework.request[1] = 1’b1. // issue read operation @1 memsys. // check for grant readOp(address1. data1). // get random 8-bit address data1 = randVar1[29:22].46 Chapter 5. address0).clk).20 memsys. // release request @2.grant == 2’b00. // check for release @1 memsys. // issue read operation @1 memsys. } } join 5.clk).20 memsys.20 memsys. // check for grant region_exit(regId.20 memsys. address1). .

. When this construction takes place. when calling a method. or object. each of the methods described below should be included within class CPU: class CPU { property declarations. Further. it is useful to simplify our port declarations. Constructors Objects.vr file (this is only advisable in small examples where working with a single file is easy). and a class’s subroutines are referred to as methods. constraint definitions. when calling a class method. the new() method within the class is executed (if any exists). Using this constructor. Memory System 47 In our system. A class’s data is referred to as properties. } Encapsulation A class is a collection of data and a set of subroutines that act on that data. For ease of use. method definitions. or instance. you must identify the instance name for which the method is being called.. the specified arguments are passed to the new task within the class. the interface specification generated using Vera’s template generator is included in the main memsys. .method_name(). you must use this syntax: instance_name. This declaration creates an instance (called instance_name) of class class_name. argument2. are created when a class is instantiated using the new statement: class_name instance_name = new(). This is because each method only accesses the properties associated with its object.Tutorial Chapter 5. you can allow for runtime customizing of the object: class_name instance_name = new(argument1. The conventions for these arguments are the same as for Vera subroutine calls. Port Assignment When implementing object-oriented concepts into our system. Because multiple instances of classes can exist. Class properties are instance-specific. argumentN). you can initialize the class upon construction or instantiation. by passing arguments to the constructor. By defining a new task within the class. So. Each instance of a class has its own copy of the variables declared in the class definition. . or instances. These comprise the contents of a class instance.

localarb = arb. We must then create the read and write operation methods. we must create the initialization method that is executed when the class is constructed. grant memsys. this time the bind is passed to the object so that we do not have to account for it in the declaration.\n”). Class Methods In our class. It is also helpful to create methods to request and release the bus.48 Chapter 5.grant[0]. we declare two binds. grant. . The initialization method new is: task new (bus_arb arb) { printf(“Constructing new CPU. one for each CPU: bind bus_arb arb0 { request memsys. } bind bus_arb arb1 { request memsys. However. grant memsys.grant[1].request[1]. } Our read operation readOp must behave as before. The initialization method should pass in the bind of type bus_arb (as declared above) and assign it to a local property. Confidential and Proprietary Synopsys Inc. Memory System Tutorial We define a bus arbiter virtual port bus_arb to be used with each CPU.request[0]. } Using this virtual port declaration. It has a request and a grant signal: port bus_arb { request. } These binds are passed to the class methods to determine which signals are affected by method calls.

memsys. data). The writeOp method is: task writeOp() { @1 memsys.busAddr = address. // request the bus @2. the bind is passed to the object so that we do not have to account for it in the declaration.\n”).busData = data.adxStrb = 1’b0.$request = 1’b1.RdWr_ = 1’b0. data). if (localarb == arb0) printf(“CPU0 is writing. printf(“READ address = 0%H. memsys.20 localarb. // check for grant } . memsys.busData == data.busRdWr_ = 1’b1.busRdWr_ = 1’b1. @2. } Our request_bus method must assert the corresponding request line and check for the appropriate grant line: task request_bus() { @1 localarb. data = 0%H \n”.busData = 8’bzzzzzzzz. note the conditional statement that evaluates the bind passed to the object and prints which CPU is writing. memsys. } Our write operation writeOp must behave as before. memsys. memsys. @1 memsys. else if (localarb == arb1) printf(“CPU1 is writing. address.Tutorial Chapter 5. @1 memsys.5 memsys. Memory System 49 The readOp method is: task readOp() { @1 memsys.$grant == 1’b1. address. printf(“WRITE address = 0%H.adxStrb = 1’b1. data = 0%H \n”.busAddr = address.adxStrb = 1’b0. memsys. Again. However.adxStrb = 1’b1.\n”).

// release the bus @2. the randomize() function returns a 1. Earlier. The randomize() class method generates random values for all random variables within the specified class instance. Using random declarations.. If it does not. Memory System Tutorial Conversely. printf(“delay = %d/n”. we generated a random delay using the random() system function.20 localarb.clk). Each time an instance is randomized.$request = 1’b0. task delay_cycle() { repeat(delay) @(posedge memsys.$grant == 1’b0.delay). you must specify the instance for which the system function is called: function int object_name. it returns a 0. Because randomize() acts as a class method. .50 Chapter 5. If an object has no random variables anywhere in its inheritance hierarchy (no random variables or sub-objects) or if all of its random variables are inactive. object_name .. . Variables declared as random within a class are randomized when the randomize() system function is called. } Confidential and Proprietary Synopsys Inc. // check for grant } Random Variables You can declare class properties as random using the rand declaration: rand data_type variable = initial_value.randomize().The object_name is the name of the object in which the random variables have been declared. our release_bus method must release the corresponding request line and check for the appropriate grant line: task release_bus() { @1 localarb. the address and data values for that instance are randomized. we implement the same delay as a class method delay_cycle: rand integer delay. The randomize() method returns a 1 if it successfully sets all the random variables and objects to valid values. Using random variables. data. we declare our class properties address and data as random: rand bit[7:0] address.

including precedence. random_variable . we must instantiate each object and invoke our initialization routines. It is a series of expressions that are enforced when the class is randomized.The random_variable parameter specifies the variable to which the constraint is applied. and wrap-around. truncation. regId. ==. Memory System 51 Note that there are no restrictions on the value that delay can assume because it is declared as an integer.address). >. =?=.randomize(). >=.The constraint_name is the name of the constraint block.The constraint expression where: • Constraints can be any OpenVera expression with variables and constants of type bit. the same execution sequence created using fork/join can be written as: regId = alloc(REGION. // check if address is free .The valid operators for constraints are: <. • Constraint expressions follow Verilog syntax and semantics. fork { repeat(256) { errflag = cpu0. 0. and !?=. With our class CPU defined with the properties and methods described above.Tutorial Chapter 5. or enumerated type. cpu0. ===. region_enter(WAIT. !==. REGION. <=.The constraint_expressions are the conditional expressions that limits random values. integer. CPU cpu1 = new(arb1). • Constraint expressions are evaluated bidirectionally that is. !=. constraint_expression . regId). 1). associativity. We can implement constraints on the values that random variables can assume using the constraint construct: constraint constraint_name { contraint_expressions } constraint_name . operator . trace(ON. both sides of the equation are solved simultaneously. Implementing OOP Before we can use our objects. sign extension. Constraint expressions are of the form: random_variable operator expression. expression . which specify the binds to pass to the objects: CPU cpu0 = new(arb0).

writeOp().release_bus(). regId. } } Note how the class property address is passed (using the instance name). Confidential and Proprietary Synopsys Inc. First. instantiation and sampling.address). cpu1. you specify a coverage definition through the coverage_group directive. region_enter(WAIT. Please see the User Guide for defining embedded coverage groups. cpu0. Additionally coverage goals and the sample event can be defined. region_exit(regId. Memory System Tutorial cpu0. The definition includes valid and invalid states and transitions that are monitored through out the simulation.request_bus().address).release_bus(). 5. As an introduction to Vera’s functional coverage. cpu1.request_bus(). region_exit(regId. cpu1. // check if address is free cpu1. cpu0. cpu0. cpu1. and coverage reports. cpu0. cpu1.readOp(). cpu1.release_bus().address). cpu1. Also note the ease of reuse through invoking the class methods for the appropriate instance name.writeOp().52 Chapter 5.2. cpu0. cpu1.request_bus().readOp(). cpu0. .randomize(). cpu0. a subset of the coverage features is discussed here: state and transition declarations. } } { repeat(256) { errflag = cpu1.delay_cycle().request_bus(). Note – For the purposes of the tutorial we will be working with coverage group definitions external to a class definition.4 Functional Coverage Vera’s functional coverage capabilities are available in both an external coverage definition as well as within the OOP framework.delay_cycle().release_bus().

In its simplest form it can be: sample variable_name [. We will not be using cross coverage in the tutorial . variable_name].] sample_event_definition. } argument_list: The arguments are parameters passed at instantiation. where variable_name is the name of the Vera variable or signal name that is sampled by the coverage_group. Memory System 53 Coverage Definition The basic syntax for defining a coverage_group is: coverage_group definition_name [(argument_list)] { sample_definitions. the sample construct has the form: sample variable_name { [state_or_transition_definition]. [attribute_definition]. where sampled_variable_list is a comma separated list of the sampled variables of the coverage_group. They have the same conventions as subroutine arguments and can have their default values set within the declaration.Tutorial Chapter 5. In its simplest form it can be: cross cross_name (sampled_variable_list). When defining state and or transitions for sampled variables. attribute_definitions. It is declared using the sample construct of a coverage_group. [cross_definitions. sample_definition: A sample_definition defines the variables and/or DUT signals that are sampled by the coverage_group. } cross_definition: You can define cross coverage of variables sampled in a coverage_group using the cross construct.

and value_expression is a Vera expression. where attribute_name is the name of the attribute. Actual arguments are passed in to the coverage_group instance as parameters to the new task call. and passed-by-reference. You can define three kinds of parameters in the coverage_group’s definition: sampled. The event_expression can be an expression such as @([specified_edge] interface_signal). Confidential and Proprietary Synopsys Inc. You define a sampling event for the coverage_group as follows: sample_event = event_expression. The User Guide details the attributes that can be specified at the coverage_group level.54 Chapter 5. The coverage_group samples all of its sampled variables and updates the appropriate bins when the sampling event triggers. sync(). // Passed-in sample parameter } Sampled Variable State and Transition Bins If you do not define any state or transition bins for a sampled variable. Sampled parameters are preceded by the sample keyword in the formal parameter list of the coverage_group definition. or wait_var(). This provides an easy-to-use mechanism for binning different values of a sampled variable. and their default values. Argument list What if your coverage model cuts across your class abstraction and all of the elements of your coverage model do not reside in the same class? You can pass arguments to a coverage_group in order to address this need. The coverage_group construct optionally allows for the declaration of formal parameters. coverage_group MyCov(sample bit[3:0] paramVar){ sample_event = @(posedge CLOCK). passed-by-value. Vera automatically creates state bins for you. In the following example coverage_group MyCov defines a sampled parameter paramVar that is sampled at every positive edge of the system clock. For the purposes of the tutorial we will be using sampled variables. They are treated like a constant “var” argument passed to a task. You can specify an attribute’s value as follows: attribute_name = value_expression. Memory System Tutorial sample_event_definition: You must specify a sampling event expression in the coverage group definition. attribute_definition: You can use attributes for controlling various aspects of a coverage_group. . // Sample event sample paramVar.

the bin counter is incremented one.Tutorial Chapter 5. when entered.The exps can be any valid coverage expression. The state specification is a list of elements (separated by commas) that are matched against the current value of the state variable. Each element of the state specification should be an expression. You cannot call functions in the expression. a single state or multiple states are associated with a monitor bin via a state specification. Illegal or bad states are those states in the design that.The state_bin_name is the base name of the state bins being created. The expressions are evaluated when the coverage object is instantiated. When the m_state declaration is used. multiple state bins are created. state_bin_name . Illegal state declarations associate illegal states with a bin. When the state variable matches the expression. In a state declaration. illegal states. They associate bins with these activities and monitor how many times these activities occur within a simulation. If you would like more information on auto bin creation. The syntax is: bad_state error_bin_name (state_specification). The m_state state declaration is used to declare multiple state bins up to a maximum of 4096 bins. Memory System 55 You can either let Vera automatically create state bins for a sampled variable or explicitly define named state and/or transition bins for each of the sampled variables. Each named bin groups a set of values (state) or a set of value transitions (trans) associated with a sampled variable. refer to the Vera User Guide. any matches increment the bin counter by one. For the current cycle. State Declarations Coverage declarations are used to declare legal states. . covering all the values in the range. The expressions can include variables. exp . For the tutorial we will be user-defining the state and transitions bins. result in verification errors. legal transitions. The syntax for a state declaration is: state state_bin_name (state_specification). The syntax is: m_state state_bin_name (exp1:exp2). and illegal transitions.

However. This statement increments the specified bin counter every time the state variable matches a value not defined in the state declarations. you can use the not state specification: bad_state error_bin_name (not state). When the specified edge occurs. The sampling event expression allows you to control when the object takes a sample. the object is sampled.. sync events and OVA events. Coverage objects can be triggered on clock edges. Confidential and Proprietary Synopsys Inc. it is often useful to monitor all transitions that have not been defined as legal transitions. For the purposes of the tutorial we will be sampling on Clock and signal edges. . variable changes. signal edges. The syntax is: bad_trans trans_bin_name (state_transitions). Memory System Tutorial The state specification can be any expression or combination of expressions as in the state declarations. it is often useful to define every state that is not in the state declarations as a bad state. Defining Sample Events You must specify a sampling event expression in the coverage group definition. For such instances. This will be used for all instantiations of a coverage definition. Coverage objects can be sampled on clock or signal edges as per the synchronization command. bad_trans trans_bin_name (not trans). Declaring a sequence of transitions between states specifies state transitions. The general format is: trans trans_bin_name (state_set_1 -> state_set_2 -> . The state transition can be any state transition set valid for transition declarations. ->state_set_N). Vera uses the not trans argument.56 Chapter 5. For a complete description of other sample events please refer to the Vera User Guide. Illegal transition declarations associate an illegal transition with a monitor bin. To use that definition of bad states. Transition Declarations Transition declarations associate state transitions with monitor bins. The counter associated with the specified bin will be incremented every time a transition occurs that is not explicitly defined in the transition declaration.. The syntax for transition declarations is: trans trans_bin_name (state_transitions) conditional. However. The syntax is: sample_event = @([specified_edge] interface_signal | CLOCK).

input[1:0] cntrlr_state INPUT_EDGE verilog_node “dut. which is passed in at the time of instantiation via a sampled variable. state WRITE1(3). bad_state (not state). Because we are monitoring a new signal. In our system. } Implementing Coverage Groups We have added a memory controller probe that acts as a state machine for the memory controller. trans t1 ("IDLE" -> "START"). and WRITE1 to IDLE. we must first identify the states and transitions we want to check. state WRITE0(2). coverage_group cov1 { sample_event = @(posedge CLOCK).cntlr_state. START to IDLE. trans t3 ("START" -> "WRITE0"). The signal that it monitors is memcntlr_probe. trans t2 ("START" -> "IDLE"). trans t4 ("WRITE0" -> "WRITE1"). state START(1). we will monitor the state variable cntrlr_state. our coverage definition is: coverage_group cntlr_cov(sample bit[1:0] cntlr_state) { sample_event = @(posedge CLOCK). trans t5 ("WRITE1" -> "IDLE"). } } . sample g_var. sample cntlr_state { state IDLE(0). START (1).state”. bad_trans (not trans). and WRITE1 (3) and the transitions IDLE to IDLE. we must add a new interface: interface memcntrlr_probe { input clk CLK. IDLE to START. Assuming we have states IDLE (0). WRITE0 to WRITE1. all instances of coverage group cov1 will be sampled upon the posedge of the system clock. } To implement a coverage object that monitors the states and transitions of our memory controller. WRITE0 (2). Memory System 57 In the following example. START to WRITE0. trans t0 ("IDLE" -> "IDLE").Tutorial Chapter 5.Umem.

Vera will generate a coverage database: memsys_test. Memory System Tutorial We also want to check that the entire address space is tested. The choice is to generate a hyperlinked HTML report or a text based report.cntlr_state). sample peek { m_state (0:255).db We have two options for reviewing the data within the database.db more memsys_test. . So we must modify the adxStrb signal declaration within the interface specification such that the signal is defined as bidirectional (inout) with the proper input and output edges: inout adxStrb OUTPUT_EDGE INPUT_EDGE OUTPUT_SKEW.58 Chapter 5.db netscape memsys_test. range cov2. It is important to note that Vera cannot directly sample output signals. cov2 = new(memsys.adxStrb). we must declare the objects within our main program: cntlr_cov cov1. We monitor the state variable peek to check that it assumes all valid states between 0 and 255: coverage_group range(sample bit[7:0] peek) { sample_event = @( negedge memsys. both involve generating a coverage report and analyzing the results.txt Confidential and Proprietary Synopsys Inc. Analysis of Coverage Results Following the simulation run. } } Before we can instantiate our coverage objects. we must instantiate them within the main program. so we instantiate them before the forks: cov1 = new (memcntlr_probe.html& Text report: vera –cov_text_report memsys_test. HTML report: vera –cov_report memsys_test.busAddr). With our objects declared. We want our objects to monitor the activity in our forked processes.index.

opens with Netscape text report.Tutorial Chapter 5. opens in more Figure 5-2 shows a typical example of an HTML coverage report. Memory System 59 Note – The Makefile for this tutorial has two options for generating reports. Please select either of the following two options: make html# make text# HTML report. Figure 5-2 Coverage Rerport .

which turns off an event.. we want to write data to the bus and then read it to check that it was correctly written to memory. Without running the simulation in lockstep. The syntax to call the sync() task is: task sync(keyword sync_type. the only trigger_types we use are ON. Memory System Tutorial 5.. mailboxes allow us to read only addresses that have been written to previously. The sync() system task synchronizes statement execution to one or more triggers. Events act as the go-between for triggers and syncs. Implementing Triggers In our system. and triggers. a process blocks until another process sends a trigger to unblock it.The event_name is the event being triggered. This allows us to verify that data is correctly written to memory.] event event_name). Further.In our system. Note that we must return to the for loop to ensure lockstep behavior (each iteration of the loop of a process depends on the trigger/sync call of the other process): Confidential and Proprietary Synopsys Inc. This requires that we advance the simulation in lockstep. eventN . .To simplify our design. Triggers allow us to advance the simulation in lockstep such that one CPU writes data to the bus and the other reads that data before the next write.2. CPU0 must issue only write requests while CPU1 issues only read requests. event_name . Triggers send events.The event is the event variable name on which the sync is activated. To do this. CPU1 must only read data after CPU0 completes a successful write. Triggers Triggers refer to a process that involves events. and OFF. event event1. Syncs suspend a process until a trigger activates to unblock the process. Note that you must declare your event variables within the scope that the sync/trigger combination is used. event2. When you call a sync. which blocks until all events are triggered.60 Chapter 5. To do this. that process is suspended until a trigger sends an event that unblocks the sync. trigger_type . we use triggers within our fork/join block. we only use the ALL sync. When a sync is called. eventN). . syncs. Events are variables that synchronize concurrent processes. sync_type . Mailboxes allow us to perform a similar test using random addresses. which turns on an event.5 Interprocess Communication In addition to regions. The syntax to call a trigger is: function int trigger([keyword trigger_type. we can use triggers and mailboxes to control our concurrent processes.

done). cpu1.readOp(index1. int mailbox_count). Vera automatically generates a mailbox ID. mailbox_id .index1). However. When you use 0. Similarly.request_bus().The mailbox_id is the ID number of the particular mailbox being created. cpu0. go_ahead). } } { for(index1=0. It must be an integer value. done). index1. index1++) { address1=index1. trigger(OFF. sync(ALL. if the letter has not been delivered when you check the mailbox. mailboxes behave like real mailboxes. Memory System 61 event go_ahead. Vera’s mailboxes allow you to transfer and retrieve data in a very controlled manner. trigger(ON. The syntax is: function int alloc(MAILBOX. To allocate a mailbox. you must choose whether to wait for the letter or retrieve the letter on subsequent trips to the mailbox. done). .release_bus().writeOp(address0. index1 <= 255. go_ahead). When a letter is delivered and put into the mailbox. go_ahead). fork { for(index0=0.Tutorial Chapter 5. cpu1. you must use the alloc() system function. index0++) { address0 = index0. integer index0. Conceptually. trigger(OFF. cpu0. Data can be sent to a mailbox by one process and retrieved by another. cpu0. } } join Mailboxes A mailbox is a mechanism to exchange messages between processes. index0 <= 255. trigger(ON. you can retrieve the letter (and any data stored within).address0). cpu1.request_bus(). done. sync(ALL.release_bus().. int mailbox_id. . You should generally use 0..

mailbox_id . dest_var . It must be an integer value. The mailbox_put() system task stores data in a mailbox in a FIFO manner. If there is a type mismatch between the data sent to the mailbox and the destination variable. mailbox_id . the function waits for a message to be sent. check_option . The mailbox_get() system function returns data stored in a mailbox. Instead. The syntax is: function scalar mailbox_get(keyword wait_option. If the CHECK option is active. the function returns a 0. but it does not dequeue an item from the mailbox. int mailbox_id [. we can have CPU0 write to random addresses. If the mailbox is empty. In our implementation. The syntax is: task mailbox_put(int mailbox_id. Otherwise.The check_option is an optional argument that should be set to CHECK when used.The wait option can either be NO_WAIT or WAIT.The mailbox_id specifies which mailbox data is being retrieved from. Memory System Tutorial mailbox_count . Note that when passing objects. the function returns the number of entries in the mailbox. that address is sent to the mailbox and read by CPU1 so that CPU1 knows which addresses are valid to read.62 Chapter 5. It specifies whether type checking occurs between the mailbox data and the destination variable. Each time it writes to an address. scalar dest_var [. including the entry just received. we want to store the address and data after a write. it returns 0.The data can be any general expression that evaluates to a scalar. The mailbox_put() system task sends data to the mailbox.The mailbox_id specifies which mailbox receives the data. and the message is left in the mailbox and is dequeued on the next mailbox_get() function call. The mailbox_get() system function assigns any data stored in the mailbox to the destination variable and returns the number of entries in the mailbox. a runtime error occurs unless the CHECK option is used. . depending on the wait option. keyword check_option]]). only object handles are passed through the mailbox. Meanwhile. The code for this configuration is: Confidential and Proprietary Synopsys Inc. Implementing Mailboxes Using mailboxes.The dest_var is the destination variable of the mailbox data. a -1 is returned.The mailbox_count specifies how many mailboxes you want to create. scalar data). The WAIT option suspends the process until a message is sent to the mailbox. If the wait option is NO_WAIT. the other CPU waits until an address and data are stored before it reads from the bus. If no destination variable is specified. The alloc() function returns the base mailbox ID if the mailboxes are successfully created. we do not need to run the simulation in lockstep. wait_option . The NO_WAIT option continues code execution if the mailbox is empty. data .

cpu1.CHECK).address). cpu1. cpu0. cpu0.address.Tutorial Chapter 5.request_bus().release_bus().data).CHECK). } } { repeat(256) { success = mailbox_get(WAIT. } } join . Memory System 63 mboxId = alloc(MAILBOX.readOp(address.mboxId. success = mailbox_get(WAIT. mailbox_put(mboxId. 1). 0.randomize(). mailbox_put(mboxId.request_bus(). fork { repeat(256) { errflag=cpu0. cpu1.release_bus(). cpu0.mboxId.writeOp().

64 Chapter 5. Memory System Tutorial Confidential and Proprietary Synopsys Inc. .

Sign up to vote on this title
UsefulNot useful