Config_db set method in tb_top can have different implementations, which is best?

I have looked at different environments, and they use the method in different ways, for ex:


// 1: allowing access to all testbench components
// I understand this isn't best practice as it would increase searching time, when using get().
uvm_config_db #(virtual intf)::set(null,"*","inf0",inf0); // @ tb_top

// 2: sending to driver and monitor directly
// I understand this isn't best practice, as a config class could be sent to agent, and 
// depending on some of its values, the agent makes different tweaks to the driver and monitor?
uvm_config_db #(virtual intf)::set(null,"uvm_test_top.m_env.m_agent_0.m_driver","inf0",inf0); // @ tb_top
uvm_config_db #(virtual intf)::set(null,"uvm_test_top.m_env.m_agent_0.m_monitor","inf0",inf0);// @ tb_top

// 3: sending to env, then to agent.
// How about this? is it really needed to pass by env?
uvm_config_db #(virtual intf)::set(null,"uvm_test_top.m_env","inf0",inf0); // @ tb_top
uvm_config_db #(virtual intf)::set(this,"m_agent_0","inf0",inf0); // @ env

// 4: sending directly to required agents without passing by env.
// I think this is the best practice?
uvm_config_db #(virtual intf)::set(null,"uvm_test_top.m_env.m_agent_0","inf0",inf0); // @ tb_top

So, what is the best practice, that I can generalize in any environment?
I am grateful for any extra insights or corrections. Thank you.

In reply to tonyyalfred:

The recommended approach is to target uvm_test_top. The reason for this is that the test is responsible for creating all environment/agent configuration objects, of which the virtual interface handle should be part of.

After creating the configuration objects, the test should get the vif handle using the correct name, assign it to the configuration object, then pass the configuration object to the environment using the config_db.

In reply to cgales:

The tb_top module should pass the virtual interface only to the test, so use the path to the test: uvm_test_top.

// module tb_top
uvm_config_db #(virtual intf)::set(null, "uvm_test_top", "inf0", inf0);

In the test class, put the virtual interface in an agent configuration object. That way the test passes a single value (the handle) and not a dozen different values.

Next, the test class should use a relative path to the agent. The first argument to set() should be this, which points to the test object.

Finally, end the agent’s path string with a wildcard so it matches the agent, driver, monitor, and sequencer.

// test class
agt_cfg0 = agent_config::type_id::create("agt_cfg0");
agt_cfg0.inf = inf0;
agt_cfg0.active = UVM_ACTIVE;
uvm_config_db #(agent_config)::set(this, "m_env.m_agent_0*", "cfg", agt_cfg0);