UVM Monitor
The UVM monitor is a passive component in a UVM testbench. Its primary responsibility is to observe activity on the interface connected to the Design Under Verification (DUV) and convert it into transaction-level data (sequence items). Unlike the driver, the monitor does not actively drive any signals on the interface. It simply observes and collects data.
Your Roadmap
Diagram of UVM Monitor
+-----------------------+
| UVM Environment |
| |
| +------------------+ |
| | Agent | |
| | +--------------+ | |
| | | Driver | | |
| | +--------------+ | |
| | +--------------+ | |
| | | Monitor | | |
| | +--------------+ | |
| | +--------------+ | |
| | | Sequencer | | |
| | +--------------+ | |
| +------------------+ |
| |
| +------------------+ |
| | Analysis Port | |
| +------------------+ |
+-----------------------+
|
v
+----------------------+
| DUT |
+----------------------+
Why Use a Dedicated Monitor?
- Separation of Concerns: Decouples observation logic from stimulus generation (handled by the driver and sequence). This makes the testbench more modular and maintainable.
- Reusability: Monitors can be reused across different tests and even different projects, as long as the interface remains the same.
- Protocol Checking: Monitors can perform protocol checks to ensure that the DUV is behaving correctly.
- Data Collection for Coverage and Analysis: Monitors collect transaction data that can be used for functional coverage analysis, debugging, and performance analysis.
Key Concepts of UVM Monitors
- Monitor Class (
uvm_monitor
): A base class for creating monitor components. - Transaction Collection: Monitors convert signal activities into transactions.
- Passive Observation: Monitors do not influence the DUT; they only observe.
- Data Reporting: Monitors report collected transactions to analysis ports connected to other components.
Structure of a UVM Monitor:
A UVM monitor is a class that extends uvm_monitor
. It typically contains the following key elements:
- `uvm_component_utilsĀ Macro: This macro is essential for registering the monitor with the UVM factory and enabling other UVM features.
- Interface Handle: A handle to the interface connected to the DUV.
- Analysis Ports: One or more
uvm_analysis_port
instances used to broadcast collected transactions to other components (e.g., scoreboards, coverage collectors). new()
Constructor: The constructor is used to create an instance of the monitor.build_phase()
: Thebuild_phase
is used to get the virtual interface from the configuration database.run_phase()
: This phase is the main execution phase of the monitor. It’s where the monitor observes the interface and collects data.collect_transaction()
(User-Defined): A task (or function) that captures the relevant data from the interface and creates a transaction (sequence item).
Diagram 1: Monitor in the Testbench Architecture
DUV ---->| Monitor |---->| Scoreboard/ |---->| Coverage |
+---------+ | Coverage | | Collector|
| Collector | +----------+
+------------+
Example Code:
`include "uvm_macros.svh"
class my_transaction extends uvm_sequence_item; // (From previous examples)
rand bit [7:0] address;
rand bit [31:0] data;
rand bit write_enable;
`uvm_object_utils(my_transaction)
// ... (rest of the transaction class)
endclass
class my_monitor extends uvm_monitor;
`uvm_component_utils(my_monitor)
virtual my_interface vif; // Interface handle
uvm_analysis_port#(my_transaction) item_collected_port;
function new(string name = "my_monitor", uvm_component parent = null);
super.new(name, parent);
item_collected_port = new("item_collected_port", this);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual my_interface)::get(this, "", "vif", vif))
`uvm_fatal("CFG", "Failed to get vif from config_db");
endfunction
task run_phase(uvm_phase phase);
forever begin
@(posedge vif.clk); // Example clocking
if (vif.valid) begin
my_transaction trans = my_transaction::type_id::create("trans");
collect_transaction(trans);
item_collected_port.write(trans); // Broadcast the transaction
end
end
endtask
task collect_transaction(my_transaction trans);
trans.address = vif.address;
trans.data = vif.data;
trans.write_enable = vif.write_enable;
endtask
endclass
Diagram 2: Monitor Class Structure
+-----------------+
| my_monitor |
+-----------------+
| `uvm_component_|
| utils(my_mon) |
| vif |
| item_collected_|
| port |
| new() |
| build_phase() |
| run_phase() |
| collect_trans()|
+-----------------+
Explanation:
- The monitor declares an
uvm_analysis_port
to broadcast collected transactions. - In the
run_phase
, the monitor continuously observes the interface, triggered by a clock edge (in this example). - When a valid transaction is detected on the interface, the
collect_transaction()
task is called to capture the relevant data. - The collected transaction is then broadcast to other components using
item_collected_port.write()
.
Defining a UVM Monitor
1. Monitor Class
The monitor class is derived from uvm_monitor
. It implements the functionality to sample the interface signals and convert them into transactions.
class my_monitor extends uvm_monitor;
`uvm_component_utils(my_monitor)
virtual my_interface vif;
uvm_analysis_port#(my_transaction) ap;
function new(string name = "my_monitor", uvm_component parent = null);
super.new(name, parent);
ap = new("ap", this);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual my_interface)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "Virtual interface not defined! Simulation aborted.")
endfunction
task run_phase(uvm_phase phase);
my_transaction tx;
forever begin
@(posedge vif.clk);
tx = my_transaction::type_id::create("tx");
// Sample interface signals and assign to transaction fields
tx.addr = vif.addr;
tx.data = vif.data;
tx.write_enable = vif.we;
ap.write(tx); // Send transaction to analysis port
end
endtask
endclass
Connecting the Monitor
2. Connecting the Monitor in the Environment
Monitors are typically instantiated and connected within an agent, which is part of the environment.
class my_agent extends uvm_agent;
`uvm_component_utils(my_agent)
my_monitor mon;
my_driver drv;
my_sequencer seqr;
function new(string name = "my_agent", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
mon = my_monitor::type_id::create("mon", this);
drv = my_driver::type_id::create("drv", this);
seqr = my_sequencer::type_id::create("seqr", this);
endfunction
endclass
Benefits of UVM Monitors
- Passive Observation: Monitors observe and report on signal activities without influencing the DUT.
- Data Collection: They convert raw signal activities into structured transactions.
- Reusability: Monitors can be reused across different testbenches and projects.
- Modularity: Clear separation of concerns, making the testbench modular and easier to manage.
Key Takeaways:
- The monitor observes interface activity and converts it into transactions.
- It uses
uvm_analysis_port
to broadcast collected transactions. - The
collect_transaction()
task implements the data capture logic. - Monitors promote modularity, reusability, and maintainability.
- Monitors are passive components and do not drive any signals on the interface.