当前位置: 首页 > 工具软件 > interface > 使用案例 >

UVM高级应用之interface

颛孙子民
2023-12-01

1.

interface中除了可以定义变量,还可以定义任务和函数。还可以在其中使用always和initial语句。

interface可以代替driver做很多事情,但是并不能代替driver做所有的事情。interface只适用于做一些低层次的转换,如8b10b转换、曼彻斯特编码等。这些转换动作是与transaction完全无关的。

使用interface代替driver的第一个好处是可以让driver从底层繁杂的数据处理中解脱出来,更加专注于处理高层数据。第二个好处是有更多的数据出现在interface中,这会对调试起到很大的帮助。

不过,当使用interface完成这些转换后,如果想构造这些转换异常的测试用例,则稍显麻烦。如构造一个8b10b转换的错误,需要在interface中加入一个标志位err_8b10b,根据此标志位的数据决定向数据线上发送何种数据。
而如果这种转换是在driver完成的,有两种选择,一是在正常的driver中加入异常driver的处理代码;二是重新编写一个全新的异常driver,将原来的driver使用factory机制重载掉。
无论是哪种方式都能实现其目的。相比来说,在interface上实现转换能够更有助于调试,这一优势完全可以弥补其劣势。

2.

一般可能需要三种可变时钟:

  • 第一种是在不同测试用例之间时钟频率不同,但是在同一测试用
    例中保持不变。
  • 第二种是在同一个测试用例中存在时钟频率变换的情况。类似PLL在时钟稳定前的过渡期,我们不关心这段过渡期时,而只关心过渡期前和过渡期后的时钟频率。
  • 第三种可变时钟和第二种很像,但是它既关心过渡期前后的时钟,也关心PLL在过渡期的行为。

实现可变时钟,可以使用config_db,在测试用例中设置时钟周期:

function void my_case0::build_phase(uvm_phase phase);
	uvm_config_db#(real)::set(this, "", "clk_half_period", 200.0);
endfunction

在top_tb中使用config_db::get得到设置的周期:

initial begin
	static real clk_half_period = 100.0;
	clk = 0;
	#1;
	if(uvm_config_db#(real)::get(uvm_root::get(), "uvm_test_top", "clk_half_period", 	clk_half_period))
	`uvm_info("top_tb", $sformatf("clk_half_period is %0f", clk_half_per iod), UVM_MEDIUM)
	forever begin
        #(clk_half_period*1.0ns) clk = ~clk;
	end
end

这里采用之前说过的非线性的设置和获取,由于config_db::set是在0时刻执行,而如果
config_db::get也在0时刻执行,那么可能无法得到设置的数值,所以在top_tb中,在config_db::get前有1个时间单位的延迟。

以上只适用第一种可变时钟的情况。对于第二种可变时钟,可以使用如下方式:

initial begin
	static real clk_half_period = 100.0;
	clk = 0;
	fork
		forever begin
			uvm_config_db#(real)::wait_modified(uvm_root::get(), "uvm_test_to p", 				"clk_half_period");
			void'(uvm_config_db#(real)::get(uvm_root::get(), "uvm_test_top", " 						clk_half_period", clk_
				`uvm_info("top_tb", $sformatf("clk_half_period is %0f", clk_half_p 				eriod), UVM_MEDIUM)
		end
		forever begin
			#(clk_half_period*1.0ns) clk = ~clk;
		end
	join
end

在测试用例中可以随着时间的变换而设置不同的时钟:

task my_case0::main_phase(uvm_phase phase);
	#100000;
	uvm_config_db#(real)::set(this, "", "clk_half_period", 200.0);
	#100000;
	uvm_config_db#(real)::set(this, "", "clk_half_period", 150.0);
endtask

但是,使用这种config_db的方式很难实现第三种可变时钟。要实现第三种时钟,可以专门编写一个时钟接口:

interface clk_if();
	logic clk;
endinterface

在top_tb中实例化这个接口,并在需要时钟的地方以如下的方式引用:

clk_if cif();
dut my_dut(.clk(cif.clk),
.rst_n(rst_n),

为可变时钟从uvm_component派生一个类:

class clk_model extends uvm_component;
	`uvm_component_utils(clk_model)
	virtual clk_if vif;
	real half_period = 100.0;
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		if(!uvm_config_db#(virtual clk_if)::get(this, "", "vif", vif))
			`uvm_fatal("clk_model", "must set interface for vif")
		void'(uvm_config_db#(real)::get(this, "", "half_period", half_perio d));
		`uvm_info("clk_model", $sformatf("clk_half_period is %0f", half_peri od), 			UVM_MEDIUM)
	endfunction
	virtual task run_phase(uvm_phase phase);
		vif.clk = 0;
		forever begin
			#(half_period*1.0ns) vif.clk = ~vif.clk;
		end

在env中,实例化此类:

class my_env extends uvm_env;
	clk_model clk_sys;
	virtual function void build_phase(uvm_phase phase);
		clk_sys = clk_model::type_id::create("clk_sys", this);
	endfunction
endclass

在这种使用方式中,时钟接口被封装在了一个component中。在需要新的时钟模型时,只需要从clk_model派生一个新的类,然后在新的类中实现时钟模型。使用factory机制的重载功能将clk_model用新的类重载掉。通过这种方式,可以将时钟设置为任意想要的行为。

参考自张强《UVM实战》

公众号:程序员Marshall

 类似资料: