The Easier UVM Code Generator Tutorial Part 1: Getting Started
Using the Code Generator on a Project
You can use the code generator in several different ways:
- You can use the code generator simply as a learning aid by generating and running working examples of UVM code that are based on your particular DUT. You can define template files that represent the interfaces of your specific DUT, run the code generator, then provide driver and monitor code to implement the protocols and thus convince yourself and your management that getting a UVM environment up-and-running is not so difficult!
- You can use the code generator to create a framework for your production code, then throw away the code generator templates and extend and maintain the production code without re-running the code generator ever again. Although the code generator is only used in the initial stages of the project and is then discarded, you still have the benefit that all the generated code starts out with a consistent structure.
- You can use the code generator to create a framework for your production code, then use a combination of include files and script files to extend and modify the functionality of the generated code according to the specific needs of the project. If you implement this approach carefully, you should be able to re-run the code generator at any stage to reflect any changes you make to the template files, but this does depend on you not making any modifications to the generated code that cannot be reproduced at will using include files or scripts, which might not be trivial to do. Using the code generator for as long as possible into a project means you can continue to take of advantage of the code generator to create the boilerplate code that connects everything together.
Here we have a series of tutorials that will build up your knowledge of the code generator step-by-step. Amongst many other things, you will learn how to include user-defined code within the generated code.
Before you start, make sure you have the followed the installation instructions to install the necessary Perl modules. If you don't, you will see the following error message when you run the code generator:
Can't locate File/Copy/Recursive.pm
The Tutorial Example
We will start with the simplest example possible. To run the code generator at all you will need an absolute minimum of three template or control files: an interface template file, a common template file, and a pinlist file. We also have two very short files to define the DUT itself, which is just a stub in this minimal example. We will also add two very short script files, one to run the generator and a second to run simulation, simply to avoid having to remember the command lines and their arguments.
clkndata.tpl Interface template file common.tpl Common template file pinlist Pinlist file mydut/mydut.sv SystemVerilog source file for the DUT gen Script to run the code generator run Script to run the simulator
You can download the files for this example (the directory named ./minimal), or because they are so short, you can simply copy-and-paste the contents from this webpage.
Interface template file
THE FIRST TEMPLATE FILE we need is the interface template file. This file characterizes one DUT interface, specifying such things as the variables in a UVM transaction and the variables in the SystemVerilog interface. The code generator requires at least one interface template file that must specify at least one variable in the associated transaction class and interface. For this minimal example we have an interface named clkndata that transfers one byte of data to the DUT on the active edge of a clock signal:Filename clkndata.tpl
agent_name = clkndata trans_item = data_tx trans_var = rand byte data; if_port = logic clk; if_port = byte data; if_clock = clk
The agent_name clkndata will be used as the root for many of the filenames and SystemVerilog names generated, such as clkndata_agent, clkndata_if, clkndata_pkg, and clkndata_config. Note that the template filename itself is the agent name with the file extension .tpl. In practice you would have one such template file for each DUT interface, each containing a unique agent name.
The trans_item data_tx will be used as the class name of the class that extends uvm_sequence_item to define the transaction used within the generated agent, that is, the transaction sent from the sequencer to the driver and the transaction sent out through the analysis port of the monitor.
The items after the "=" on the trans_var line, that is rand byte data;, will be included in the generated transaction class. Note the ";" at the end of the line: this is important is it will literally be copied into the generated code. Also do remember to include the rand keyword if you want the variable randomized. There will typically be multiple trans_var lines, but we will keep it to one for the sake of showing a minimal example.
The items after the "=" on the if_port lines, that is logic clk; and byte data;, will be included in the generated SystemVerilog interface. Again, note the ";" at the end of the lines. Even for a minimal example we need a clock as well as a data signal.
Finally, if_clock picks out one of the interface variables as the clock signal. The name given as the if_clock must be one of the if_ports in this same template file. The order of the lines in the template file is unimportant, though it will make more sense to the human reader if you order the lines as shown here.
(See the Reference Guide for a full list of the settings that can be given in the interface template files.)
Common template file
THE SECOND TEMPLATE FILE we need is the common template file, which must be named common.tpl. The code generator always requires a common.tpl file, which specifies some common settings. There is only one mandatory setting, namely dut_top, but there are a large number of optional settings that give you a lot of control over the generated code. Many of the settings have sensible default values, so you can ignore these settings as you get started.Filename common.tpl
dut_top = mydut
dut_top specifies the name of the top-level SystemVerilog module of the DUT, which is mydut in this case. The DUT source files must be placed in a directory named ./dut, or you can choose a different directory name using the common template file setting dut_source_path, or you can list the locatations and compilation order of the files manually in a single file named files.f.
Here is the DUT file used in this minimal example:Filename mydut/mydut.sv
module mydut (input clk, input byte data); always @(posedge clk) $display("mydut data = %h", data); endmodule
(See the Reference Guide for a full list of the settings that can be given in the common template file.)
THE THIRD TEMPLATE/CONTROL FILE is the pinlist file, which specifies the connections between the pins (ports) of the DUT (as specified in the SystemVerilog source code of the DUT) and the variables of the DUT interfaces (as specified in the interface template files). You can change the name of this file using the common template file setting dut_plist.Filename pinlist
!clkndata_if clk clk data data
The pinlist file is divided into sections, one section per DUT interface, each section starting with an exclamation mark "!" followed by the name of the interface, which will always be the agent name plus the suffix _if. In this minimal example there is only one agent clkndata, so the corresponding interface name is clkndata_if. Each of the remaining lines contain the name of a port of the top-level module of the DUT followed by the name of a variable in the corresponding DUT interface, e.g. data data.
You can connect DUT ports that are not part of any specific interface using an unnamed section in the pinlist file, for example:
! clock_port global_clock_var reset_port global_reset_var
You can also pass SystemVerilog parameters to the DUT in the pinlist file.
(See the Reference Guide for a full definition of the pinlist file.)
Run the code generator
Having provided the three template/control files (clkndata.tpl, common.tpl, pinlist) and the DUT file (mydut.sv), we can now run the code generator for this minimal example. We will put the command line in a one-line script file:Filename gen
perl ../easier_uvm_gen.pl clkndata.tpl
The perl script easier_uvm_gen.pl can be anywhere: we are just locating it from the script using a relative path. The names of any interface template files are included on the command line. The name of the common template file defaults to common.tpl (but can be set from the command line using the -m switch.)
(See the Reference Guide for a full list of command line flags.)
We can now run the code generator:
/home/user/minimal > gen Easier UVM Generator: version 2015-11-16 Parsing Templates ... Reading: clkndata.tpl Writing code to files generating testbench writing simulator script to generated_tb/sim directory Code Generation complete
The generated structure looks like this. The best way to figure out exactly what the code generator does is to run it yourself!
top_tb (module) ↳ top_th (module instance) ↳ clkndata_if (interface instance) mydut (module instance) top_config (class uvm_object) ↳ top_test (object, class uvm_test) ↳ top_env (uvm_env) ↳ clkndata_config (uvm_object) clkndata_agent (uvm_agent) ↳ clkndata_sequencer clkndata_driver (uvm_driver) clkndata_monitor (uvm_monitor) clkndata_coverage (uvm_subscriber) ↳ top_default_seq (created in run_phase, class uvm_sequence) ↳ clkndata_default_seq (uvm_sequence) ↳ data_tx (uvm_sequence_item)
Run the simulation
Having run the code generator, we can now run a simulation immediately, out-of-the-box. Again, we will put the command lines into a script file:Filename runius
cd generated_tb/sim compile_ius.shFilename runquesta
cd generated_tb/sim vsim -c -do "do compile_questa.do; run -all"Filename runvcs
cd generated_tb/sim compile_vcs.shFilename runriviera
cd generated_tb/sim vsimsa -do compile_riviera.do
generated_tb is the default name for the directory containing the generated files, but you can change that by adding a project = line to the common template file. The ./sim directory contains scripts to run the Cadence, Mentor, Synopsys, and Aldec simulators (correct when this tutorial was written, but tools keep changing, of course). (See the Reference Guide for a fuller description of compilation and simulation.)
The generated verification environment will run a sequence that generates just a single transaction, and so somewhere in the middle of your simulation log you should see a UVM_INFO message from clkndata_driver that prints out the value of the data field in the transaction, something like: The generated verification environment will run a sequence that generates just a single transaction, and so somewhere in the middle of your simulation log you should see a UVM_INFO message from clkndata_driver that prints out the value of the data field in the transaction, something like:
# UVM_INFO ../tb/clkndata/sv/clkndata_driver.sv(47) @ 0: uvm_test_top.m_env.m_clkndata_agent.m_driver [clkndata_driver] req item # vseq.seq.req # data = f4
However, you will not see this data value being printed out from the DUT (from the line $display("mydut data = %h", data);) because we have not provided an implementation of the driver, so although the driver is receiving a transaction from the sequencer, it is not actually wiggling the pins of the DUT interface.
Implement the driver
In order to wiggle some pins in the DUT interface, we need to complete the implementation of the driver. We can do this by providing the implementation of a task that is already being called by the generated driver code, and then use the template files to instruct the code generator to include our task. Using this technique, we will not need to repeat the edit every time we re-run the code generator: our task gets picked up automatically.Filename include/clkndata_do_drive.sv
task clkndata_driver::do_drive(); vif.data <= req.data; @(posedge vif.clk); endtask
We have to be careful to use the correct naming conventions within this task. clkndata is the agent name from the interface template file, but the suffix _driver, the task name do_drive, the virtual interface name vif, and the tranasaction variable name req are all fixed by the code generator, so we have to be sure to use exactly these names. The task would do whatever pin wiggling is necessary to drive a transaction onto the DUT interface, synchronizing with any timing signals and consuming time ( @(posedge vif.clk) ) as necessary.
To have the code generator include our task, need to add the name of the specific file to be included in the corresponding interface template file. We can include files to implement the driver, the monitor, and the coverage collection subscriber for each agent, thus avoiding the need to manually hack the code each time it is regenerated. Any include files must be placed in a directory named ./include, or you can choose a different directory name using the common template file setting inc_path.(See the Reference Guide for a fuller description of extending the generated code.)Filename clkndata.tpl
... driver_inc = clkndata_do_drive.sv ...
With the driver implementation in place, we can now simply re-run the code generator and the simulator using the same scripts as before. The simulator log now shows a little more action:
# mydata data = f4
This message demonstrates that our dummy DUT is now receiving a transaction through the clkndata interface as a result of the driver wiggling pins. However, the verification environment is still only sending one single transaction to the DUT.
Send more than one transaction
The code generator generates a default sequence for the agent that sends a single transaction to the driver, and also a top-level virtual sequence that contains a loop that starts the child sequence a number of times as given by the m_seq_count variable. This variable has a default value of 1, so by default only one transaction is generated, but this value can easily be changed using a setting in the common template file.Filename common.tpl
dut_top = mydut top_default_seq_count = 10
The only effect of this top_default_seq_count setting is to change a loop counter in the default top-level virtual sequence, which can be useful when getting simple examples up-and-running. As you extend the generated code you would eventually replace this default top-level virtual sequence with your own user-defined sequence, making the top_default_seq_count setting irrelevant.
Now simply run-run the scripts, and you will see the DUT receiving 10 transactions instead of 1.
Easier UVM Coding Guidelines
Introduction to the Easier UVM Coding Guidelines
Summary of the Easier UVM Coding Guidelines
Detailed Explanation of the Easier UVM Coding Guidelines
Easier UVM Glossary
Easier UVM Coding Guidelines - Download
Easier UVM - Deeper Explanations
Coverage-Driven Verification Methodology
Requests, Responses, Layered Protocols and Layered Agents
How to Access a Parameterized SystemVerilog Interface from UVM
Easier UVM Code Generator
Easier UVM Code Generator - Download
Easier UVM Code Generator - Tutorial Part 1: Getting Started
Easier UVM Code Generator - Tutorial Part 2: Adding User-Defined Code
Easier UVM Code Generator - Tutorial Part 3: Adding the Register Layer
Easier UVM Code Generator - Tutorial Part 4: Hierarchical Verification Environments
Easier UVM Code Generator - Tutorial Part 5: Split Transactors
Easier UVM Code Generator - Frequently Asked Questions (FAQ)
Easier UVM Code Generator - Reference Guide
Easier UVM Video Tutorial
Introducing Easier UVM
Easier UVM - The Big Picture
Key Concepts of the Easier UVM Code Generator
Running Easier UVM in EDA Playground
Easier UVM - Components and Phases
Easier UVM - Configuration
TLM Connections in UVM
Easier UVM - Transaction Classes
Easier UVM - Sequences
Easier UVM - Tests
Easier UVM - Reporting
Easier UVM - Register Layer
Easier UVM - Parameterized Interfaces
Easier UVM - Scoreboards
The Finer Points of UVM Sequences (Recorded Webinar)
UVM Run-Time Phasing (Recorded Webinar)
A YouTube playlist with all the above videos and more
Easier UVM Paper and Poster
Easier UVM - Coding Guidelines and Code Generation - as presented at DVCon 2014
Easier UVM Q&A Forum
Easier UVM Google Group
Easier UVM Examples Ready-to-Run on EDA Playground
Minimal example with driver
Minimal example with coverage in a subscriber as well as driver and monitor.
Minimal example with register sequence and register block
Example with four interfaces/agents, two of which use a register model.
Minimal example with dual-top modules and split transactors
Minimal example showing a UVM sequence getting information from the config database
Minimal example showing features of objections and the command line processor
Minimal example showing the reporting features of UVM.
Example that drops an objection when coverage exceeds some threshold
Example that sends a response transaction from the driver back to the uvm_reg_adapter
Example that uses a frontdoor sequence to pass a response object back to the register sequence that called read/write
Example of a parameterized interface generated from an Easier UVM interface template file
Example that pulls in a user-defined parameterized interface
Example of a reference model with the Syosil scoreboard
Back to the full list of UVM Resources