最近在做项目的时候,发现自己写的代码不是很好,一个 function 或者 task 要好几百行,
主要原因就是,在写代码的时候,想到哪就写到哪,用到之前哪写过的代码就直接粘过来用,
以前还没有什么感觉,甚至觉得这样做还有好处,
想想将一部分代码封装成 function 或者 task,如果抽象程度很高,立意不是很明确,在环境里面调来调去,很容易发生混乱,哪有将代码直接贴上来显得直观,一行一行的代码顺序捋下来,反而能让人更快理解这部分干了什么。
这完全是,坐井观天,没见过好代码,而产生的错误想法,尤其是看过师傅的代码,每个 function 或者 task 都很简短,立意明确,层层相扣,丝毫不乱。从组件中的 run_phase 一路抽丝剥茧地捋下来,很丝滑,反观自己臃肿的代码简直没眼看。
我们在做完测试点分解和验证方案撰写后,不应该风风火火的着急写代码。我们在动笔写代码前,虽然已经在验证方案中确定好了验证的思路,理清了项目的整体脉络,但是我们在写组件的时候,还是要好好思考其中的细节。其实每个 function 和 task 都是值得反复推敲的,只有将整体和细节都把握住,才能做到下笔如有神。敲代码只是所有验证工作的一小环,好好思考如何将活给干好,才是验证工程师的主要工作。
将代码封装成 function 或者 task,可以有效缩短原本 function 或者 task 的代码行数,但不是其最主要的作用。复用,才是其精髓所在,将一个操作,或者一类操作封装成一个 function 或者 task,就可以在组件中很多地方被调用,这就是打包思想。
实际中,我们 function 或者 task 很少只封装一个操作,而是一类。这和我们日常生活经验很像,我们打包东西很少只是包一个东西,而是将相似的东西都包在一起,然后打个标签,表示这是什么类东西。
当我们封装 function 或者 task 时,这一类操作的量,其实是我们需要关注的,我们不能想着什么都往里塞,一个包,包天下万物。当包中的东西越来越多时,导致 function 或者 task 的抽象程度越来越高,反而其立意越来越模糊,丧失了可读性,这是不可取的。这也是封装 function 或者 task 的难点,考验验证人员的功力。
function 或者 task 抽象程度大,其参数也就越来越多,我们可以使用结构体打包参数。我们打包参数的首要准则也是要立意清晰,让人一看到这个参数包就明白其含义,而不是每次要去看包里面都包了啥。
下面是一个简单的示例代码
class my_test extends uvm_test;
typedef enum int {READ, WRITE} option_e;
typedef struct packed{
bit a1;
bit a2;
bit a3;
option_e option;
} a_info;
`uvm_component_utils(my_test)
extern function new(string name = "my_test", uvm_component parent = null);
extern virtual task run_phase(uvm_phase phase);
//如果结构体中的成员作为参数,有input 和 output两种类型,则结构体参数要声明为inout
extern virtual function void change_info(inout a_info a);
endclass
function my_test::new(string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
task my_test::run_phase(uvm_phase phase);
a_info a;
a.a1 = 1;
change_info(a);
`uvm_info("VAR_PRT", $sformatf("a_info is %p", a), UVM_LOW)
endtask : run_phase
function void my_test::change_info(inout a_info a);
a.a2 = 1;
a.option = a.option.next(1);
endfunction : change_info