介绍
OpenTitan是一个谷歌的开源RISC-V项目,这里借用了项目中的uvm环境生成脚本uvmdvgen来生成环境。
使用流程是通过命令后缀指定环境和agent名称,然后脚本依据路径下的mako模板替换生成环境,脚本基于python实现。
项目链接:
GitHub - lowRISC/opentitan: OpenTitan: Open source silicon root of trust
使用步骤
环境
脚本基于python实现,github目录有环境要求,个人在centos7 python 3.14测试。
脚本在util/路径,-h可以看到帮助说明,-a用于生成agent,-s指示agent主从分开,-e用于生成env,-c从CIP库生成否则从DV库生成,-hr、hi、ha指示包含寄存器模型、中断和alert,-ea指示env中包含的agent,-ao指示agent生成在路径,-ro指示env生成在的路径。
$ util/uvmdvgen.py -h
usage: uvmdvgen.py [-h] [-a] [-s] [-e] [-c] [-hr] [-hi] [-ha]
[-ea agt1 agt2 [agt1 agt2 ...]] [-ao [hw/dv/sv]]
[-eo [hw/ip/<ip>]] [-v VENDOR]
[ip/block name]
生成agent
使用脚本生成环境包括2步,首先通过-a生成agent,再通过-e -ea生成集成agent的env。
在端口通过指令生成i2c agent,这里是生成在当前目录,也可以加-ao指定目录。
$ util/uvmdvgen.py i2c -a
在i2c/i2c_agent可以看到生成文件。

但实际生成的是纯空文件,或者说替换了名字的空模板,内容还要自己从头写,所以指令agent可以是任何名称,例如i3c、i4c,不一定是DV库中的agent。
在项目的hw/dv/sv路径中有一些uvm agent,包含jtag、uart、spi、i2c等,是有完整内容的,可以直接拷贝到自己的环境。
生成env
在vrf目录生成一个环境,包含axi apb pcie agent,和前相似agent名可以任意。
util/uvmdvgen.py vrf -e -ea axi apb pcie
在vrf/dv路径下可以看到生成环境,这个脚本还是比较自动的,首先env中可以看到例化各个agent,传递了配置,连接了scb,连接了top_seqr。不过也有缺点,同名agent只能例化1次否则要手动修改,还有就是没有ref model和对应scb连接。

其他vseq、vseqr不再赘述,tb中例化了agent同名if,并config_db发送到了agent中,基本流程都有实现。
脚本实现
脚本通过python实现,可以看一下实现方法。
uvmdvgen.py
脚本接收了参数,并根据-a还是-e决定执行gen_agent还是gen_env。
if args.gen_agent:
gen_agent.gen_agent(args.name, args.has_separate_host_device_driver, args.agent_outdir, args.vendor)
if args.gen_env:
gen_env.gen_env(args.name, args.is_cip, args.has_ral, args.has_interrupts, args.has_alerts,args.num_edn, args.env_agents, args.env_outdir, args.vendor)
gen_agent.py
首先将agent内文件定义成包含元组的列表,按照路径、agent名、文件名和后缀形式保存。
agent_srcs = [(agent_dir, name + '_', 'if', '.sv'),
(agent_dir, name + '_', 'item', '.sv'),
(agent_dir, name + '_', 'agent_cfg', '.sv'),
(agent_dir, name + '_', 'agent_cov', '.sv'),
(agent_dir, name + '_', 'monitor', '.sv'),
(agent_dir, name + '_', 'driver', '.sv'),
(agent_dir, name + '_', 'host_driver', '.sv'),
(agent_dir, name + '_', 'device_driver', '.sv'),
(agent_dir, name + '_', 'agent_pkg', '.sv'),
(agent_dir, name + '_', 'agent', '.sv'),
(agent_dir, name + '_', 'agent', '.core'),
(agent_dir, "", 'README', '.md'),
(agent_dir + "/seq_lib", name + '_', 'seq_list', '.sv'),
(agent_dir + "/seq_lib", name + '_', 'base_seq', '.sv')]
然后通过for循环读出,使用mako组件,根据名称找到同路径下预存的.tpl模板,然后在模板文件中替换名称。
for tup in agent_srcs:
path_dir = tup[0]
src_prefix = tup[1]
src = tup[2]
src_suffix = tup[3]
ftpl = src + src_suffix + '.tpl'
fname = src_prefix + src + src_suffix
# read template
tpl = Template(filename=str(importlib.resources.files('uvmdvgen') / ftpl))
with open(path_dir + "/" + fname, 'w') as fout:
fout.write(
tpl.render(name=name,
has_separate_host_device_driver=
has_separate_host_device_driver,
vendor=vendor))
实际替换过程均在模板中实现,以driver.sv.tpl为例,模板就是空的,所以生成agent也是空文件,如有一些通用需求,可以直接修改.tpl模板。
class ${name}_driver extends dv_base_driver #(.ITEM_T(${name}_item),
.CFG_T (${name}_agent_cfg));
`uvm_component_utils(${name}_driver)
// the base class provides the following handles for use:
// ${name}_agent_cfg: cfg
gen_env.py
和gen_agent.py相似,替换过程均在模板中实现,env的模板更复杂一些,所以生成出来的不是空文件,以env.sv.tpl为例,会根据agent列表for循环生成集成的代码。例如有添加ref model的需求,直接修改模板文件即可。
function void build_phase(uvm_phase phase);
super.build_phase(phase);
% for agent in env_agents:
// create components
m_${agent}_agent = ${agent}_agent::type_id::create("m_${agent}_agent", this);
uvm_config_db#(${agent}_agent_cfg)::set(this, "m_${agent}_agent*", "cfg", cfg.m_${agent}_agent_cfg);
cfg.m_${agent}_agent_cfg.en_cov = cfg.en_cov;
% endfor
endfunction
总结
脚本定位还是生成模板,不能直接运行,agent也只是壳子,需要再人工完成。不过毕竟不是专门生成环境的工具,实现过程比较清晰,适合写平台脚本参考。