register automation

从验证的角度看,register automation 流程一般如下:

  1. 按照一定格式编写寄存器文件
  2. 使用工具解析寄存器文件,生成verilog代码、test bench代码、c代码、说明文档
  3. 使用已生成的uvm reg文件,搭建寄存器模型

tools

csrCompiler是Semifore公司的寄存器自动产生工具。它支持输入csr语法文件或是csv格式excel文件,输出verilog文件、VHDL文件、word文档、HTML文件、c语言头文件、验证tb文件。

例如

输入文件csv

可得到verilog文件

也可得到doc文件

当然也可以运行如下命令,得到uvm reg文件

csrCompiler -t uvm demo.csv

uvm tb

拿到自动生成的uvm reg文件,里面会包含xxx_reg和demo_reg_block。

在base_test里面增加例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class base_test extends uvm_test;
...
svt_ahb_reg_adapter reg_adapter;
demo_reg_block demo_csr;
demo_virtual_sequencer vsqr;
...
function void build_phase(uvm_phase phase);
demo_csr = demo_reg_block::type_id::create("demo_csr");
demo_csr.add_hdl_path("top.dut");
demo_csr.build();
demo_csr.lock_model();
reg_adapter = svt_ahb_reg_adapter::type_id::create("reg_adapter");
endfunction
function void connect_phase(uvm_phase phase);
svt_ahb_master_configuration cfg;
svt_configuration get_cfg;

vsqr.ahb_sqr = env.ahb_env.master[0].sequencer;
demo_csr.default_map.set_sequencer(vsqr.ahb_sqr, reg_adapter);
vsqr.demo_csr = demo_csr;
vsqr.ahb_sqr.get_cfg(get_cfg);
if(!$cast(cfg, get_cfg))
`uvm_error("error");
reg_adapter.p_cfg = cfg;
endfunction
...
endclass
1
2
3
4
5
class demo_virtual_sequencer extends uvm_sequencer;
svt_ahb_master_transaction_sequencer ahb_sqr;
demo_reg_block demo_csr;
...
endclass

如果是在test里面,直接调用write就可以了

1
2
uvm_status_e status;
this.demo_csr.xx.write(status,1234,UVM_FRONTDOOR);

如果是在seq里面,就通过sqr来调用

1
2
uvm_status_e status;
p_sequencer.demo_csr.xxx.write(status,1234,UVM_FRONTDOOR);

前门写,会反映到总线上。

前门读,会反映到总线上,就算返回的值和预期不一样,也不会报错,比如先写1234,再读出来是4,也不会报错。

后门读,就直接访问内部信号了,如果没有这个内部信号,就会报错

1
[UVM/DPI/HDL_GET] get: unable to locate hdl path(top.dut.xxx)

最后来看下函数原型

read任务的原型是

1
2
3
4
5
6
7
8
9
extern virtual task read(output uvm_status_e status,
Output uvm_reg_data_t value,
input uvm_path_e path=UVM_DEFAULT_PATH,
input uvm_reg_map map=null,
input uvm_sequence_base parent=null,
input int prior =-1,
input uvm_object extension-null,
input string fname="",
input int lineno=0);

第一个参数表示读操作是否成功,第二个表示读取的数值,第三个是读取的方式,有UVM_FRONTDOOR和UVM_BACKDOOR。

write任务的原型是

1
2
3
4
5
6
7
8
9
extern virtual task read(output uvm_status_e status,
input uvm_reg_data_t value,
input uvm_path_e path=UVM_DEFAULT_PATH,
input uvm_reg_map map=null,
input uvm_sequence_base parent=null,
input int prior =-1,
input uvm_object extension-null,
input string fname="",
input int lineno=0);

第一个参数表示读操作是否成功,第二个表示写入的数值,第三个是写入的方式,有UVM_FRONTDOOR和UVM_BACKDOOR。

另外还有两个后门操作,peek和poke。它们和write、read的区别是。wr函数会模仿dut的行为,而pp完全不管dut的行为。如对一个只读的寄存器进行写操作,write函数是写不进去的,而peek可以写进去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
task uvm_reg::poke(output uvm_status_e status,
input uvm_reg_data_t value,
input string kind="",
input uvm_sequence_base parent=null,
input uvm_object extension=null,
input string fname="",
input int lineno=0);

task uvm_reg::peek(output uvm_status_e status,
output uvm_reg_data_t value,
input string kind="",
input uvm_sequence_base parent=null,
input uvm_object extension=null,
input string fname="",
input int lineno=0);

镜像值,是最大可能与dut同步的值

期望值,是要写入的值。

Read/write操作,无论前门后门操作完成都会更新镜像值和期望值

Peek/poke操作,无论前门后门操作完成都会更新镜像值和期望值

get/set操作,set操作会更新期望值,但镜像值不会变化。get操作会返回寄存器模型中的期望值。

update操作,会见检查期望值和镜像值是否一致,如果不一致,就将期望值写入dut,并且更新镜像值,使其与期望值一致。