Lexical conventions in Verilog are similar to C in the sense that it contains a stream of tokens. A lexical token may consist of one or more characters and tokens can be comments, keywords, numbers, strings or white space. All lines should be terminated by a semi-colon ;
.
Verilog is case-sensitive, so var_a and var_A are different.
Comments
There are two ways to write comments in Verilog.
- A single line comment starts with
//
and tells Verilog compiler to treat everything after this point to the end of the line as a comment. - A multiple-line comment starts with
/*
and ends with*/
and cannot be nested.
However, single line comments can be nested in a multiple line comment.
// This is a single line comment
integer a; // Creates an int variable called a, and treats everything to the right of // as a comment
/*
This is a
multiple-line or
block comment
*/
/* This is /*
an invalid nested
block comment */
*/
/* However,
// this one is okay
*/
// This is also okay
///////////// Still okay
Blocking
Blocking assignment statements are assigned using =
and are executed one after the other in a procedural block. However, this will not prevent execution of statments that run in a parallel block.
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a = 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
b = 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c = 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
d = 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
e = 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
Note that there are two initial
blocks which are executed in parallel when simulation starts. Statements are executed sequentially in each block and both blocks finish at time 0ns. To be more specific, variable a gets assigned first, followed by the display statement which is then followed by all other statements. This is visible in the output where variable b and c are 8'hxx in the first display statement. This is because variable b and c assignments have not been executed yet when the first $display
is called.
ncsim> run [0] a=0xda b=0xx c=0xx [0] a=0xda b=0xf1 c=0xx [0] a=0xda b=0xf1 c=0x30 [0] d=0xaa e=0xx [0] d=0xaa e=0x55 ncsim: *W,RNQUIE: Simulation is complete.
Before we look at more details of the Verilog language, it would be good to understand the different layers of abstraction in chip design.
The top layer is the system level architecture that defines the various sub-blocks and groups them based on functionality. For example, a processor cluster would have multiple cores, cache blocks, and cache coherence logic. All of this will be encapsulated and represented as a single block with input-output signals.

A typical design flow follows a structure shown below and can be broken down into multiple steps. Some of these phases happen in parallel and some sequentially. We'll take a look at how a typical project design cycle looks like in the industry today.

Requirements
A customer of a semiconductor firm is typically some other company who plans to use the chip in their systems or end products. So, requirements of the customer also play an important role in deciding how the chip should be designed. Naturally, the first step would be to collect the requirements, estimate the market value of the end product, and evaluate the number of resources required to do the project.
The UVM configuration database accessed by the class uvm_config_db
is a great way to pass different objects between multiple testbench components.
Click here to refresh on config database !
Methods
There are two primary functions used to put and retrieve items from the database which are set()
and get()
respectively.
static function void set ( uvm_component cntxt,
string inst_name,
string field_name,
T value);
static function bit get ( uvm_component cntxt,
string inst_name,
string field_name,
inout T value);
Rules
- Create or update a config setting for field_name in inst_name from cntxt
- The setting is made at cntxt with the full scope being {cntxt, ".", inst_name}
- If cntxt is
null
, then inst_name provides the complete scope information of the setting - field_name is the target field
- Both inst_name and field_name may be glob style or regular expression style expressions
- Settings from hierarchically higher levels have higher precedence
- Settings from the same level of hierarchy have a last setting wins semantic
How to debug uvm_config_db ?
The best way to understand how the combination of cntxt, inst_name and field_name works is by enabling the commandline debug +UVM_CONFIG_DB_TRACE
switch for UVM that dumps information on all the set()
and get()
calls within a simulation.
$> irun <all_other_options> +UVM_CONFIG_DB_TRACE
Example
We'll look at how two testbench environments behave when set
and get
methods are called from different hierarchies.
1. Test and Env
To understand how config_db evaluates expressions, we'll set up a small testbench structure with an empty environment as shown below. An expression is set from the test class and retrieved in the environment's build_phase

We set cntxt to null
and inst_name to uvm_test_top to indicate that all components in the test can access the item. To keep things simple, we'll put a string
item tagged as Friend.
class base_env extends uvm_env;
...
string name;
virtual function void build_phase (uvm_phase phase);
super.build_name ();
// Retrieve the string that was set in config_db from the test class
if (uvm_config_db #(string) :: get (null, "uvm_test_top", "Friend", name))
`uvm_info ("ENV", $sformatf ("Found %s", name), UVM_MEDIUM)
endfunction
endclass
class base_test extends uvm_test;
...
base_env m_env;
virtual function void build_phase (uvm_phase phase);
...
// Set this string into config_db
uvm_config_db #(string) :: set (null, "uvm_test_top", "Friend", "Joey");
endfunction
endclass
It is quite clear that the first argument cntxt cannot be anything other than a uvm_component
object. From the following simulation log, we can see that when +UVM_CONFIG_DB_TRACE
is passed as a command-line switch, simulation will dump all function calls to set
and get
into the log. However, the lines of our interest are higlighted in color, the yellow one representing a set
call and the green representing a successful get
call. The concatenation of cntxt, inst_name and field_name match for both set and get calls and hence the database successfully finds and returns the string tagged as "Friend".
ncsim> run UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(392) @ 0: reporter [UVM/RELNOTES] ---------------------------------------------------------------- UVM-1.2 (C) 2007-2014 Mentor Graphics Corporation (C) 2007-2014 Cadence Design Systems, Inc. (C) 2006-2014 Synopsys, Inc. (C) 2011-2013 Cypress Semiconductor Corp. (C) 2013-2014 NVIDIA Corporation ---------------------------------------------------------------- UVM_INFO @ 0: reporter [RNTST] Running test base_test... UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/GET] Configuration 'uvm_test_top.base_env.recording_detail' (type logic signed[4095:0]) read by uvm_test_top.base_env = null (failed lookup) UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/GET] Configuration 'uvm_test_top.base_env.recording_detail' (type int) read by uvm_test_top.base_env = null (failed lookup) UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/SET] Configuration 'uvm_test_top.Friend' (type string) set by = (string) "Joey" UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/GET] Configuration 'uvm_test_top.base_env.m_agent0.recording_detail' (type logic signed[4095:0]) read by uvm_test_top.base_env.m_agent0 = null (failed lookup) UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/GET] Configuration 'uvm_test_top.base_env.m_agent0.recording_detail' (type int) read by uvm_test_top.base_env.m_agent0 = null (failed lookup) UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/GET] Configuration 'uvm_test_top.base_env.m_agent1.recording_detail' (type logic signed[4095:0]) read by uvm_test_top.base_env.m_agent1 = null (failed lookup) UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/GET] Configuration 'uvm_test_top.base_env.m_agent1.recording_detail' (type int) read by uvm_test_top.base_env.m_agent1 = null (failed lookup) UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/GET] Configuration 'uvm_test_top.Friend' (type string) read by = (string) "Joey" UVM_INFO testbench.sv(36) @ 0: uvm_test_top.base_env [ENV] Found Joey UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] --- UVM Report Summary --- ** Report counts by severityCase #2
Here, we'll make a slight modification to the set
method, rest of the code being the same. The first argument cntxt gets this
pointer, while inst_name is empty and still we get the same path "uvm_test_top.Friend" after concatenation.
uvm_config_db #(string) :: set (this, "", "Friend", "Joey");
// Same get method
if (uvm_config_db #(string) :: get (null, "uvm_test_top", "Friend", name))
`uvm_info ("ENV", $sformatf ("Found %s", name), UVM_MEDIUM)
Simulation Log ... UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/SET] Configuration 'uvm_test_top.Friend' (type string) set by uvm_test_top = (string) "Joey" ... UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/GET] Configuration 'uvm_test_top.Friend' (type string) read by = (string) "Joey"
2. Test, Env, and two Agents
Now we'll expand the environment to have a couple of agents to make things more interesting.

class base_agent extends uvm_agent;
...
virtual function void build_phase (uvm_phase phase);
if (uvm_config_db #(string) :: get (null, "uvm_test_top", "Friend1", name))
`uvm_info ("AGENT", $sformatf ("[%s] found %s", this.get_name(), name), UVM_MEDIUM)
endfunction
endclass
class base_env extends uvm_env;
...
base_agent m_agent0;
base_agent m_agent1;
...
endclass
If we run the code again with the new agents in place, you'll find that both agents also found the string tagged as "Friend1".
Simulation LogUVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/SET] Configuration 'uvm_test_top.Friend1' (type string) set by uvm_test_top = (string) "Joey" ... UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/GET] Configuration 'uvm_test_top.Friend1' (type string) read by = (string) "Joey" UVM_INFO testbench.sv(44) @ 0: uvm_test_top.base_env [ENV] Found Joey ... UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/GET] Configuration 'uvm_test_top.Friend1' (type string) read by = (string) "Joey" UVM_INFO testbench.sv(22) @ 0: uvm_test_top.base_env.m_agent0 [AGENT] [m_agent0] Found Joey ... UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_resource_db.svh(121) @ 0: reporter [CFGDB/GET] Configuration 'uvm_test_top.Friend1' (type string) read by = (string) "Joey" UVM_INFO testbench.sv(22) @ 0: uvm_test_top.base_env.m_agent1 [AGENT] [m_agent1] Found Joey
Recommended Practice
The setting in config_db is visible by only those elements that can evaluate to the same expression as the set
call. If we keep a global scope such as uvm_test_name, then all components will be able to retrieve that setting. However, this poses a potential problem of collision of field_name within the same scope, as well as retrieval of settings via unintended get
calls. Hence, the recommended practice is to make the setting available to only those components that really require the setting to function. In a similar way, during get
calls, components should look for settings that are available to them.
The expression that is set in config_db is "uvm_test_top.Friend" based on the set
function call. config_db results show the expression evaluated by the get
method call. If both of these expressions match or satisfies the glob properties, it is considered to be a match. In the case below, all the three get
method calls failed simply because the expression each of the component tried to get is different from the expression that is set
.
uvm_config_db #(string) :: set (this, "m_env.m_agent*", "Friend", "Joey"); // Set in test, available to agents uvm_config_db #(string) :: get (this, "", "Friend", name); // Get in env uvm_config_db #(string) :: get (this, "", "Friend", name); // Get in agent cntxt = uvm_test_top, inst_name = m_env.m_agent*, tag = Friend Expression set : "uvm_test_top.m_env.m_agent1*.Friend" CONFIG_DB_TRACE results: [CFGDB/SET] Configuration 'uvm_test_top.m_env.m_agent*.Friend' (type string) set by uvm_test_top = (string) "Joey" [CFGDB/GET] Configuration 'uvm_test_top.m_env.Friend' (type string) read by uvm_test_top.m_env = null (failed lookup) [CFGDB/GET] Configuration 'uvm_test_top.m_env.m_agent0.Friend' (type string) read by uvm_test_top.m_env.m_agent0 = (string) "Joey" [CFGDB/GET] Configuration 'uvm_test_top.m_env.m_agent1.Friend' (type string) read by uvm_test_top.m_env.m_agent1 = (string) "Joey"
In the above case, the setting was made available only to the agents and hence environment was not able to retrieve it at its level. To make the setting available to the environment, we'll make another set
call just for the environment. If you put a *
like m_env*, then the setting will be made available to all the components in the environment.
uvm_config_db #(string) :: set (this, "m_env.m_agent*", "Friend", "Joey"); // Set in test, available to agents uvm_config_db #(string) :: set (this, "m_env", "Friend", "Joey"); // Set in test, available to env uvm_config_db #(string) :: get (this, "", "Friend", name); // Get in env uvm_config_db #(string) :: get (this, "", "Friend", name); // Get in agent cntxt = uvm_test_top, inst_name = m_env.m_agent*, tag = Friend Expression set : "uvm_test_top.m_env.m_agent1*.Friend" cntxt = uvm_test_top, inst_name = m_env, tag = "Friend" Expression set : "uvm_test_top.m_env.Friend" CONFIG_DB_TRACE results: [CFGDB/SET] Configuration 'uvm_test_top.m_env.Friend' (type string) set by uvm_test_top = (string) "Joey" [CFGDB/SET] Configuration 'uvm_test_top.m_env.m_agent*.Friend' (type string) set by uvm_test_top = (string) "Joey" [CFGDB/GET] Configuration 'uvm_test_top.m_env.Friend' (type string) read by uvm_test_top.m_env = (string) "Joey" [CFGDB/GET] Configuration 'uvm_test_top.m_env.m_agent0.Friend' (type string) read by uvm_test_top.m_env.m_agent0 = (string) "Joey" [CFGDB/GET] Configuration 'uvm_test_top.m_env.m_agent1.Friend' (type string) read by uvm_test_top.m_env.m_agent1 = (string) "Joey"
A few examples on set
and get
methods were illustrated in the previous article. Now we'll see more combinations of expressions used to set
and get
settings from config_db and see the result of such operations on the same testbench structure with two agents in an environment.

set vs get results
To recap, a setting with field_name "Friend" is set with different cntxt and inst_name in the config_db. This is obtained by both agents and the environment using the get
method with different cntxt and inst_name combinations. If the get
call is successful, the cell is highlighted in pale green, else it is highlighted in pale red.
// Set the given field_name "Friend" in test
uvm_config_db #(string) :: set (cntxt, inst_name, "Friend", "Ross");
// Get "Friend" in env and agents
uvm_config_db #(string) :: get (cntxt, inst_name, "Friend", "Ross");
Hierarchy of each component:
Environment : uvm_test_top.m_env
Agent1 : uvm_test_top.m_env.m_agent1
Agent2 : uvm_test_top.m_env.m_agent2
The first column in the table shown below is for the test class that sets the field Friend with the given cntxt and inst_name. All subsequent columns represent different components that try to get the setting from config_db which in our example is the environment and two agents.
Test :: set | Env :: get | Agent1 :: get | Agent2 :: get |
---|---|---|---|
(null, "uvm_test_top") uvm_test_top.Friend | (this, "") uvm_test_top.m_env.Friend | (this, "") uvm_test_top.m_env.m_agent0.Friend | (this, "") uvm_test_top.m_env.m_agent1.Friend |
(this, "") uvm_test_top.Friend | (this, "") uvm_test_top.m_env.Friend | (this, "") uvm_test_top.m_env.m_agent0.Friend | (this, "") uvm_test_top.m_env.m_agent1.Friend |
(this, "*") uvm_test_top.*.Friend | (this, "") uvm_test_top.m_env.Friend | (this, "") uvm_test_top.m_env.m_agent0.Friend | (this, "") uvm_test_top.m_env.m_agent1.Friend |
(this, "m_env") uvm_test_top.m_env.Friend | (this, "") uvm_test_top.m_env.Friend | (this, "") uvm_test_top.m_env.m_agent0.Friend | (this, "") uvm_test_top.m_env.m_agent1.Friend |
(this, "m_env.*") uvm_test_top.m_env.*.Friend | (this, "") uvm_test_top.m_env.Friend | (this, "") uvm_test_top.m_env.m_agent0.Friend | (this, "") uvm_test_top.m_env.m_agent1.Friend |
(this, "m_env.m_agent*") uvm_test_top.m_env.m_agent*.Friend | (this, "") uvm_test_top.m_env.Friend | (this, "") uvm_test_top.m_env.m_agent0.Friend | (this, "") uvm_test_top.m_env.m_agent1.Friend |
(null, "uvm_test_top.m_env.m_agent1") uvm_test_top.m_env.m_agent1.Friend | (this, "") uvm_test_top.m_env.Friend | (this, "") uvm_test_top.m_env.m_agent0.Friend | (this, "") uvm_test_top.m_env.m_agent1.Friend |
(this, "*.m_agent1") uvm_test_top.*.m_agent1.Friend | (this, "") uvm_test_top.m_env.Friend | (this, "") uvm_test_top.m_env.m_agent0.Friend | (this, "") uvm_test_top.m_env.m_agent1.Friend |
(this, "m_env.m_ag*") uvm_test_top.m_env.m_ag*.Friend | (this, "") uvm_test_top.m_env.Friend | (this, "") uvm_test_top.m_env.m_agent0.Friend | (this, "") uvm_test_top.m_env.m_agent1.Friend |