System Verilog - Verification Methodology Manual
System Verilog - Verification Methodology Manual
System Verilog - Verification Methodology Manual
(VMM 1.2)
Table of Contents
1. Object Oriented Language Basics ..................................................................... 2. Layered Verification Environment ................................................................... 3. VMM Guiding Principles 4. Transactor Phasing 4.1 4.2 Explicit Phasing Implicit Phasing .......................................................................... .......................................................................... .......................................................................... ...................................................................................... 3 4 6 7 9 10 13 14 16 17 24 18 26 27 28 30 36 36
7.1 Transactor Class ...................................................................................... 7.2 Scoreboard Class ...................................................................................... 7.3 Coverage Class 7.5 Creating Tests ...................................................................................... ...................................................................................... ...................................... 7.4 Message Service ...................................................................................... 8. Hands on creating of VMM testbench elements
9. Example DUT .................................................................................................. 10. Accessing examples from the Hafez server .................................................. 11. References & Books ......................................................................................
function void display(); ..... endfunction Packet function int compute_crc(); ..... endfunction
MyPacket
To over-ride the functionality of the compute_crc in MyPacket extended class, keyword super is used to override the behavior from base class. Packet P1; My_Packet P2; P1.compute_crc (); P2.compute_crc (); When a method within the scope of the object is declared to be virtual, then the last definition of the method in the objects memory will be executed.
Polymorphism The objects of the class inherit the properties and transfer it over to the other objects.
protocol bfm = new(...); repeat (100) begin Packet p = new(); bfm.transmit(p); end
class protocol; .... task transmit(Packet pkt); ...... ....pkt.crc = pkt.compute_crc(); ..... endtask endclass
pkt
The DUT initiates the transaction and the reactive driver supplies the required data to successfully complete the transaction. For example, a program memory interface bus-functional model is a reactive driver. The DUT initiates read cycles to fetch the next instruction and the bus-functional model supplies new data in the form of an encoded instruction. The term Transactor is used to identify components of the verification environment that interface between two levels of abstraction for a particular protocol or to generate protocol transactions. In above figure, the boxes labeled Driver, Monitor, Checker and Generator are all transactors. The lifetime of transactors is static to the verification environment. They are structural components of the verification components and they are similar to the DUT modules. A driver actively supplies stimulus data to the DUT. A proactive driver is in control of the initiation and type of the transaction. A monitor reports observed high-level transaction timing and data information. A reactive monitor includes elements to generate the low-level handshaking signals to terminate an interface and successfully complete a transaction. Both transactors and data are implemented using the class construct. The difference between a Transactor class and a data class is their lifetime. Limited number of Transactor instances is created at the beginning of the simulation and they remain in existence throughout. This creates a very large number of data and transaction descriptors instances throughout the simulation and they have a short life span.
The vmm_xactor base class contains standard properties and methods to configure and control transactors. To ensure that all transactors have a consistent usage model, you must derive them from a common base class.
- Approaches
Code Macros vmm_member_begin/end vmm_callback vmm_fatal vmm_error vmm_warning vmm_note vmm_trace vmm_debug vmm_verbose vmm_unit_config vmm_rtl_config The above listings are the collection of classes and macros most commonly used. The full listing of all available classes and macros are found in VMM Standard Library User Guide.
4. Transactor Phasing
Transactors progress through a series of phases throughout simulation. All transactors are synchronized so that they execute their phases synchronously with other transactors during simulation execution. VMM supports two levels of Transactor phasing, implicit and explicit phasing. During explicit phasing, all the transactors are controlled by the master controller such as vmm_env to call the Transactor phases. In implicit phasing, the transactors execute their phases automatically and synchronously. In an implicitly phased testbench, functions and tasks representing phases are called automatically at the appropriate times. A global controller (vmm_simulation) works in conjunction with specially configured schedulers (vmm_timeline) to walk all testbench components through relevant phases. vmm_simulation acts like a conductor, keeping all of the various testbench components in sync during pre-test, test, and post-test portions of a typical simulation.
Intended Purpose Determine configuration of the testbench Create the testbench Configure options Connect TLM interfaces, channels Test specific changes Logical start of simulation Reset DUT Configuration of the DUT Logical start of the test Physical start of test Body of test, end of test detection to be done here Stop flow of stimulus Let DUT drain and read final DUT state Pass/Fail report (executed by each test) Final checks and actions before simulation termination
Explicit Transactor Phasing, transactors begin to execute when the environment explicitly calls vmm_xactor :: start_xactor to start the Transactor. This then starts the vmm_xactor :: main thread.
super.wait_for_end(); `vmm_note(log, "Running..."); #100; endtask virtual task stop (); super.stop(); `vmm_note (log, "Stopped..."); this.subenv1.stop(); this.subenv2.stop(); endtask endclass Note: Super keyword is being used to enable calling the methods from the base class instead of derived class with same method names Eg: Creation of Explicitly Phased Test using above Environment `vmm_test_begin (test, my_env, "Test") env.run(); `vmm_test_end(test) Eg: Top Program to use the above test and environment program top; initial begin my_env env = new; vmm_test_registry::run(env); end endprogram
Eg: Creation of Implicitly Phased Test using above sub_env and env
class test extends vmm_test; function new(); super.new("Test"); endfunction virtual task start_ph(); super.start_ph(); `vmm_note(log, "Started..."); endtask virtual task shutdown_ph(); super.shutdown_ph(); `vmm_note(log, "Stopped..."); endtask endclass Transactors are started during specific phases (not necessarily at start), run during a certain number of phases. Environment can suspend their execution thread and resume it and might stop it during another phase. The vmm_xactor class is the base class for transactors. It provides thread management utilities (start, stop, reset_xactor, wait_if_stopped) that are not present in the other base classes. The vmm_xactor offers both thread management and phase methods. It is important to understand to properly model transactors and how you model different behaviors at different phases. The simplest form for a Transactor is one whose behavior does not change between simulation phases. If you instantiate this Transactor in an implicitly phased environment, then it gets started by default.
12
5. Transactor Callbacks
Callback methods to monitor the data flowing through a Transactor to check for correctness inject errors or collect functional coverage metrics. You can adapt the Transactor callbacks to the needs of testcase or environment. Callback should be registered in the vmm_xactor base class. However, calling the registered callback extensions is the responsibility of the Transactor extended from the base class. Transactor Callbacks Usage Create a callback class with empty virtual methods Each virtual method represents an important stage of Transactor. The arguments of the virtual methods should contain necessary information that can be shared with the subscribers.
Eg 1 : class cpu_driver_callbacks extends vmm_xactor_callback; virtual task pre_trans (cpu_driver driver, cpu_trans tr, ref bit drop); endtask virtual task post_trans (cpu_driver driver, cpu_trans tr); endtask
endclass At every important stage in the Transactor, call the corresponding method declared above through `vmm_callback macro. Eg 2: class cpu_driver extends vmm_xactor; virtual protected task main(); super.main(); `vmm_callback(cpu_driver_callbacks, pre_trans(this, tr, drop)); if (tr.kind == cpu_trans::WRITE) begin write_op(tr); end if (tr.kind == cpu_trans::READ) begin read_op(tr); end 13
`vmm_callback(cpu_driver_callbacks, post_trans(this, tr)); endtask endclass A subscriber extends the callback class, fill the necessary empty virtual methods.
Eg 3: class cpu_sb_callback extends cpu_driver_callbacks; cntrlr_scoreboard sb; function new(cntrlr_scoreboard sb); this.sb = sb; endfunction virtual task pre_trans(cpu_driver drv, cpu_trans tr,ref bit drop); sb.cpu_trans_started(tr); endtask virtual task post_trans(cpu_driver drv, cpu_trans tr); sb.cpu_trans_ended(tr); endtask endclass Register the subscriber callback class using method vmm_xactor::append_callback. Then every time Transactor hits the defined important stages, subscriber methods will be called. Note that any number of subscribers with their own definition of virtual methods can get registered to a Transactor.
Eg 4: class cntrlr_env extends vmm_group; cpu_driver drv; virtual function void connect_ph(); cpu_sb_callback cpu_sb_cbk = new(sb); cpu_cov_callback cpu_cov_cbk = new(cov); drv.append_callback(cpu_sb_cbk); drv.append_callback(cpu_cov_cbk); endfunction endclass
6. Scenario Generators
VMM provides a scenario macro to build most of the scenario code Customize for your own needs by adding own constraints and scenario selection rules
1. Scenario Generator mainly contains 1 or more scenario classes Scenario selection class
Scenario Class will be derived from the transaction_scenario class and the scenario base class is defined by the macro. 2. Scenario Class contains - one or more scenarios selected by scenario_kind - Constraints to direct each scenario. Each scenario must be constrained with length and counter - apply() task - send the scenario to an output channel Eg: Creating a Scenario class my_scenario extends atm_cell_scenario `vmm_typename(my_scenario) int unsigned INC_VPI; // Scenario kind must be defined constrained inc_vpi_scenario { ($void(scenario_kind) == INC_VPI) -> { length == 5; repeated == 0; foreach (items[i]) if (i > 0) items[i].vpi == items[i-1].vpi+1; } } function new (); this. INC_VPI = define_scenario (Inc VPI, 5); ... endfunction ... `vmm_class_factory(my_scenario) endclass
Scenario Constraint
Registering Scenarios
1. Adding Scenario Objects to Scenario Set Eg: my_scenario my_scn = new( ); env.scn_gen.scenario_set.push_back(my_scn); //Add a new scenario Eg: my_scenario my_scn = new( ); env.scn_gen.scenario_set[0] = my_scn; // Replacing default atomic scenario
15
2. Multistream Scenario Eg: task my_ms_scenario :: execute ( ref int n ); vmm_channel to_ahb = get_channel (AHB); vmm_channel to_eth = get_channel (ETH); if (!this.ahb.randomize() ) `vmm_fatal ( log, ahb randomization failed); do begin to_ahb.put (this.ahb); end while ( this.ahb.status != ahb_cycle :: IS_OK ); to_eth.put(this.eth); n++; endtask
vmm_simulation:: run_tests()
Structural Class
Generator
Coverage
Configure
Self Check
Slave
Interface
Self-Checking Class DUT
(i) Test Class: vmm_test (ii) Structural Class: vmm_group (iii) Behavioral Class: vmm_xactor (iv) Communication Class: vmm_channel (Eg: communication between Generator and Master) 16
There are three general categories of Transactors. Master, Slave and Monitors. All of these will be built by extending from the vmm_xactor base class. Below are the examples of different types of transactors.
A typical Master Transactor waits for a transaction (typically passed in via a typed channel) to process.
class imonitor extends vmm_xactor; function new (string inst, int stream_id, vmm_object parent); super.new(monitor, inst, stream_id, parent); endfunction protected task main(); super.main(); forever begin Packet tr; this.get_input_packet(tr); this.inp_vmm_sb_ds(tr); end endtask 18
class omonitor extends vmm_xactor; function new (....); super.new(....); endfunction protected task main(); super.main(); forever begin Packet tr; this.get_output_packet (tr); this.exp_vmm_sb_ds (tr); end endtask Passive Monitors are different from the Master and Slave transactors in that they only observe interface handshakes. They do not actively participate in the interface handshake. Passive monitors exist for two primary reasons: protocol check and reconstruction of transaction to be passed on to a scoreboard. The built-in methods, inp_vmm_sb_ds () and exp_vmm_sb_ds (), can be used to check the input transaction against the expected transaction.
20
Run-Time Seeds Create Different Tests The ability to set random seed at run-time with +ntb_random_seed option is very useful for creating multiple reproduce-able tests with one simv. But user must pick and set the seed. An alternative can be +ntv_random_seed_automatic. With this option, the seed is randomly picked by vcs. Every run of the same simv binary will result in running simulation with a different seed. When using the run-time option, one must retrieve and store the seed being used with $get_initial_random_seed(). The vmm_env base class automatically calls $get_initial_random_seed() and displays the random seed for you. The default seed if neither option is applied is 1. Develop a collection of Tests class all_ports extends vmm_test; function new(string name, doc); super.new (name, doc); endfunction function void configure_test_ph( ); env.cfg.num_of_iports = 16; env.cfg.num_of_oports = 16; env.cfg.num_of_iports.rand_mode(0); env.cfg.num_of_opors.rand_mode(0); endfunction endclass all_ports test_all = new(all_ports, Testing all ports);
class ten_packets extends vmm_test; function new(string name, doc); super.new(name, doc); endfunction function void configure_test_ph( ); env.cfg.num_of_iports = 10; env.cfg.num_of_oports = 10; env.cfg.run_for_n_packets.rand_mode(0); endfunction endclass ten_packets test_ten_packets = new(ten_packets, Small Test);
21
Execution of Tests: - At test top level: program automatic test; `include tb_env.sv `include tests.inc tb_env env = new(); intial begin vmm_simulation :: list(); vmm_simulation :: run_tests(); end endprogram simv command line:
./simv +vmm_test = all_ports (These are name of the tests specified) ./simv +vmm_test = ten_packets ./simv +vmm_test = ALL_TESTS
VMM Generators VMM has two types of generators. Atomic generator and Scenario generator. Atomic generator is a simple generator, which generates transactions randomly. `vmm_atomic_gen is a macro which is used to define a class named <class_name>_atomic_gen for any user-specified class derived from vmm_data, using a process similar to the `vmm_channel macro. To use <class_name>_atomic_gen class, a <class_name>_channel must exist. <class_name>_atomic_gen generates transactions and pushes it to <class_name>_channel. A <class_name>_channel object can be passed to generator while constructing. Lets create atomic generator for auto_packet class in file auto_packet.sv 1. define `vmm_atomic_gen macro for packet class. This macro creates a packet_atomic_gen class creates and randomizes packet transactions. `vmm_atomic_gen(auto_packet,packet atomic generator) 2. define `vmm_channel for the packet class. This macro creates a packet_channel, which will be used by the packet_atomic_gen to store the transactions. Any other component can take the transactions from this channel. `vmm_channel(auto_packet) 3. Create an object of packet_atomic_gen. packet_atomic_gen pkt_gen = new ("Atomic Gen","test"); 22
4. Set the number of transactions to be generated to 4 pkt_gen.stop_after_n_insts = 4; 5. Start the generator to generate transactions. These transactions are available to access through pkt_chan as soon as they are generated. pkt_gen.start_xactor(); 6. Collect the packets from the pkt_chan and display the packet content to terminal. pkt_gen.out_chan.get(pkt); pkt.display(); Factory Class Needs Factories are blue print classes that generate objects of a specific kind A factory should be able to generate objects of the specified class Factories should also be able to generate customized transactions if specified at test case level Eg: Allocate new objects using a blueprint instance, via a virtual method class bfm extends vmm_xactor; transaction blueprint; .... task build_ph(); blueprint = new (); endtask protected task main(); super.main(); forever begin transaction tr; tr = blueprint.allocate (); ... process(tr); end endtask endclass class transaction extends vmm_data; .... virtual function vmm_data allocate; transaction txn = new(); return txn; endfucntion endclass
In above example for a factory design, we allocate from a single instance, via a virtual method, that is then stored in the local variable instead of allocating into local variable directly. In Factory class, we can replace a transaction by a derived class or we can replace a scenario by another scenario.
23
Two ways of overriding factory [i] by copy() : replace existing factory by an object of same/derived instance with the values of the instance copied. [ii] by new () : replace existing factory by a new derived object. User doesnt have to necessarily extend vmm_object or vmm_data to make use of factory service.
Scoreboard
Self-checking testbenches need scoreboards. Reusable scoreboards can be used for multiple testbenches. Proper understanding of the DUT is necessary to design an efficient scoreboard. Different score boarding mechanisms are used for different applications.
Creating & Accessing Scoreboards Use vmm_sb_ds macro If the DUT behavior is single stream and no transformation, no extension to base class is required. Synatax //Extend vmm_sb_ds class my_sb extends vmm_sb_ds; ..... endclass Eg: Use vmm_xactors scoreboard methods class bus_master extends vmm_xactor; ..... `vmm_callback(bus_master_cb, post_tr(this, tr); this.inp_vmm_sb_ds (tr); // Add input packet endclass class bus_mon extends vmm_xactor; ..... `vmm_callback (bus_mon_cb, post_tr (this, tr) ) ; this.exp_vmm_sb_ds (tr); // Check with expected packet endclass Connecting Scoreboard class my_env extends vmm_group; vmm_sb_ds sb; bus_master xtor_mstr; bus_mon xtor_mon; 24
virtual function build_ph( ); super.build ( ); this.sb = new(...); // Creating Scoreboard this.xtor_mstr = new (...); this.xtor_mon = new (...); endfunction: build_ph virtual function connect_ph( ) this.xtor_mstr.register_vmm_sb_ds (this.sb, vmm_sb_ds :: INPUT, vmm_sb_ds :: IN_ORDER) ; this.xtor_mon.register_vmm_sb_ds(this.sb, vmm_sb_ds :: EXPECT, vmm_sb_ds :: IN_ORDER); endfunction: connect_ph endclass Scoreboard Compares In addition to vmm_data :: compare() the scoreboard has two compare functions vmm_sb_ds :: quick_compare ( ) Called by expect_with_losses () & vmm_sb_ds::compare() Minimal set of checks to identify unique matches May require calling vmm_data :: compare() Default behavior returns 1 vmm_sb_ds :: compare ( ) Normal scoreboard compare routine Calls quick_compare ( ) followed by vmm_data :: compare ( ) Eg: class ahb_to_ocp_sb extends vmm_sb_ds; virtual function bit transform ( input vmm_data in_pkt, output vmm_data out_pkts[] ); ahb_tr in_tr; ocp_tr out_tr = new; $cast ( in_tr, in_pkt ); //convert in_tr to out_tr. One-to-one transform out_tr = .......... out_pkts = new [1]; 25 Overload either of these functions or custom compares Specified via virtual method vmm_ds_sb :: transform () Transform can be one-to-one, one-to-many, many-to-one Scoreboard - Transformation
26
Benefits All tests defined in program or modules Test automatically registered once declared No recompilation needed for each test Test comes with 3 timelines: pre_test, top_test, post_test
Implicit phased VMM test Overriding Options and Configurations 1. Possibility to replace environment factories and scenarios - Done in top-test timeline, before physical test start - Use vmm_test :: configure_test_ph() to add testcase specific code Example Testcase class test1 extends vmm_test; virtual function void configure_test_ph(); vmm_opts :: set_int (top.env.timeout, 10_000); // Set simulation timeout vmm_opts :: set_int (top.msgen.stop_after_n_scenarios, 50); // No. of scenarios vmm_opts :: set_int (top.cpu_chan.record, 1b0); // Turn off channel recording endfucntion endclass: test1 Test Concatenation ! %simv +vmm_test = test1 + test2 ! %simv +vmm_test = ALL_TESTS Example to write tests in the top module program top my_env env; test1 t1; test2 t2; test3 t3; initial begin env = new(env,......); t1 = new (test1); t2 = new (test2); t3 = new (test3); vmm_simulation :: run_tests ( ); end endprogram
27
4. Use make to compile and run the simulation and capture the log > make | tee log Take a look at the log file, you should see simulation is executing the sequence as shown in the tutorial. Task 2: Creating individual testbench files using vmmgen. 1. Execute vmmgen command to build individual testbench components: > vmmgen You will be prompted with the following question: Which VMM version would you be using? 1) VMM-1.1 2) VMM-1.2 Select [1-2] [Default: 2]: Specify option 2 Then you will see prompt: 1) Enter 1 to Create Complete Environment 2) Enter 2 to Generate Individual Template Again specify option 2 3) You will be prompted with: Which template do you wish to generate? Select option 1, to create a VMM transaction class. You will then be prompted with the following question: Do you want to create your own methods [Instead of vmm shorthand macros]? Select [y/Y/n/N] [Default: n]: Enter option n 4. The last question to create transaction class is: Name of transaction descriptor class? : Specify name cpu_trans A cpu_trans.sv is created in the directory. This creates a file with vmm transaction class template. You can even try to create other individual testbench components. It helps to minimize the amount of typos that would otherwise occur if you were to type all codes manually.
29
T E S T B E N C H
SRAMs
ARBITER
CONTROLLER
MEM-SYSTEM CONTROLLER
A Memory Controller block is the Design Under Test. 1. Has a master interface (CPU) and a slave interface (SRAM) with multiple device selects. 2. 1,2 or 4 SRAMs can be connected to the DUT. 3. The size of each SRAM device can be configured (256, 512, 1024) 4. Memory controller performs read and write operation on to the SRAM memory. - Master interface (cpu) instantiates the read or write operation. Slave interface (sram) responds to the read/write operations. The DUT files are located in the below format and inside hdl folder 1. arb.v // Arbiter verilog design file 2. cntrlr.v // Controller verilog design file 3. memsys.v // Memory system controller file 4. sram.v // SRAM design verilog file
30
Controller DUT
Clk
rdWr_N
ce0_n ce1_n
cntrlr
ce2_n ce3_n
ramAddr
Test data
DUT
Callbacks take place between Driver" Coverage" SRAMs TLM ports between Driver" Scoreboard" SRAMs All the design blocks need to be tested, hence separate folders are created testing individual design blocks. All the folders contains interface, top level harness, coverage and transaction files. 31
Writing Scenarios Different scenarios have been written to test the functionalities such as read/write or both through CPU onto the SRAM memory. We can find all the scenario files in the scenarios folder. For example, below we can see scenario written to randomly create read scenarios. Pay attention on the syntax used to create scenarios.
32
Creation of Makefile Makefile is being created to perform all the functions such as Compile, Run, DVE, Cleaning, Coverage and Selection of Tests. Makefile can be found at the below location > cd /packages/synopsys/setup/verification_flow/VMM/solutions
Writing Testcases: Multiple testcases are written to test the read/write features among the different design blocks and are available in the folder tests. All the individual tests include the respective scenarios to run the tests. For example, lets look at cpu_read testcase, which communicates with cpu and sram
33
Similar testcases can be written to test the different functionalities of your design. Once all the files are ready, using the Makefile explained above we can perform different operation like compile, run, coverage etc. Below command is to perform compile and run operations on design files. > make run
34
35
10. Accessing example files from Hafez Server [1] Copy the example files from the server using the below command into your present working directory > cp -rpf /packages/synopsys/setup/verification_flow/VMM/ . You can also view your present working directory path by using the command > pwd Note: We can access all the design files inside hdl folder and all the verification components such as tests, scenarios in their respective folders. 11. References & Books [1] Verification Methodology Manual for System Verilog by Janick Bergeron, Eduard Cerny, Alan Hunter and Andrew Nightingale. [2] http://www.vmmcentral.org/ [3] http://www.vmmcentral.org/vmartialarts/
36