You can use the code generator in several different ways:
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 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
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.
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:
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 as 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.)
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.
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 locations and compilation order of the files manually in a single file named files.f.
Here is the DUT file used in this minimal example:
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.
!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.)
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:
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)
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:
cd generated_tb/sim compile_ius.sh
cd generated_tb/sim vsim -c -do "do compile_questa.do; run -all"
cd generated_tb/sim compile_vcs.sh
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.
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.
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 transaction 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, we 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.)
... 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.
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.
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.