What is a uvm environment?

UVM environment is a class based component that is part of the UVM methodology. It acts as a wrapper for the underlying components and objects. It extends from the uvm_component base class and inherits all the methonds and properties from it's parent class.

Why do we need a uvm environment component?

The primary purpose of a uvm_env component extended class is to provide a heirarchical parent for all the lower components. In simple words, we can list the whys of a uvm environment component in the follwing list

  1. This is the class where different agents are instantiated with different configurations.
  2. We can set and get different config class settings into this class. Using these config objects we can decide the configuration/layout of the lower testbench.
  3. Conventionally, one instance of a UVM component acts as a single instance of a testbench. This essentially means that we can have multiple instances of the same uvm environment, but with different configurations and architectures.
Code
// Filename : basic_env.sv

class basic_env extends uvm_env;
	`uvm_component_utils(basic_env)
	
	uvm_root             top;
	
	virtual basic_if     basic_vif;
	
	basic_config         cfg;
	basic_coverage       basic_cov;
	
	basic_sequencer basic_seqr;
	
	basic_scoreboard	 basic_sb;
	basic_agent			 basic_agt;
	
	extern         function      new(string name, uvm_component parent);
	extern virtual function void build_phase(uvm_phase phase);
	extern virtual function void connect_phase(uvm_phase phase);
	extern virtual function void end_of_elaboration_phase(uvm_phase phase);
	extern virtual function void start_of_simulation_phase(uvm_phase phase);
	
	extern virtual task  main_phase(uvm_phase phase);
	
	extern virtual function void extract_phase(uvm_phase phase);
	extern virtual function void check_phase(uvm_phase phase);
	extern virtual function void report_phase(uvm_phase phase);
	extern virtual function void final_phase(uvm_phase phase);
	
	extern virtual function 	 basic_config get_cfg();
	
endclass: basic_env

//------------------------------------------------------------------//

function basic_env::new(string name, uvm_component parent);
	super.new(name, parent);

endfunction: new

//------------------------------------------------------------------//

function void basic_env::build_phase(uvm_phase phase);
	super.build_phase(phase);
	
	top = uvm_root::get();
	
	cfg = basic_config::type_id::create("cfg", this);
	
	if(!uvm_config_db#(virtual basic_if)::get(null, get_full_name(), "basic_vif", basic_vif))
    begin
		`uvm_fatal(get_type_name(), "basic interface not set")
	end
	
	// Create the coverage object if coverage enabled:

	if (cfg.coverage_enable)
    begin
		basic_cov = basic_coverage::type_id::create("basic_cov", this);
    end
	
	if(cfg.scoreboard_enable)
    begin
		basic_sb = basic_scoreboard::type_id::create("basic_sb", this);
		basic_sb.cfg = cfg;
	end

    basic_agt = basic_agent::type_id::create("basic_agt", this);
    basic_agt.cfg = cfg;
	
endfunction: build_phase

//------------------------------------------------------------------//

function void basic_env::connect_phase(uvm_phase phase);
	super.connect_phase(phase);

endfunction: connect_phase

//------------------------------------------------------------------//

function void basic_env::end_of_elaboration_phase(uvm_phase phase);
	super.end_of_elaboration_phase(phase);
	
endfunction: end_of_elaboration_phase

//------------------------------------------------------------------//

function void basic_env::start_of_simulation_phase(uvm_phase phase);
	super.start_of_simulation_phase(phase);
	
endfunction: start_of_simulation_phase

//------------------------------------------------------------------//

task basic_env::main_phase(uvm_phase phase);
	super.main_phase(phase);
	phase.raise_objection(this);
	
	phase.drop_objection(this);
endtask: main_phase

//------------------------------------------------------------------//

function void basic_env::extract_phase(uvm_phase phase);
	super.extract_phase(phase);
	
endfunction: extract_phase

//------------------------------------------------------------------//

function void basic_env::check_phase(uvm_phase phase);
	super.check_phase(phase);
	
endfunction: check_phase

//------------------------------------------------------------------//

function void basic_env::report_phase(uvm_phase phase);
	super.report_phase(phase);
	
endfunction: report_phase

//------------------------------------------------------------------//

function void basic_env::final_phase(uvm_phase phase);
	super.final_phase(phase);
	
endfunction: final_phase

//------------------------------------------------------------------//

function basic_config basic_env::get_cfg();

	return this.cfg;
endfunction: get_cfg
Theory Questions
    Yes, a uvm_env can contain another uvm_env, and this is referred to as hierarchical environments.
    The UVM environment acts as a container for all verification components like agents, scoreboards, and coverage collectors, organizing them into a reusable structure.
    Environments typically include UVM agents, scoreboards, coverage collectors, monitors, and sometimes sub-environments.
    The build_phase is used to construct and configure all the sub-components of the environment using the factory mechanism.
    Configuration data is passed using the uvm_config_db or by setting variables directly in the environment's build_phase.
    A typical environment class includes handles to sub-components (agents, scoreboards), and overrides build_phase, connect_phase, and optionally end_of_elaboration_phase.
    Agents encapsulate driver, monitor, and sequencer in a reusable unit, making the environment cleaner and more modular.
    Layered environments allow separation of concerns by grouping protocol layers, enhancing reuse and manageability in complex designs.
    By calling `uvm_factory::set_type_override_by_type()` or `uvm_factory::set_inst_override_by_type()` before the component is built.