从这里开始,将转战到Linux环境下进行仿真。原因在于我用Questa Sim在处理和DPI-C有关的问题上总是不顺利,很难找到解决方案。想到反正也要转到Linux上的,不如现在转了。
一、环境的准备
在Linux中,使用VCS和Verdi进行联合仿真。环境的搭建不妨看我的这篇博客:记一次在Ubuntu18虚拟机上安装VCS等
环境搭建好后,将之前的代码都搬移过来。
二、修改文件结构
之前的文件结构其实并不合理,在使用方面存在不便,因此在改变了环境后,正好顺便也把文件结构顺便改了。更改后的文件目录如下:
- agent
- C_model
- rtl
- amplifier
- sim
- uvm_tb
- env
- sequence
- tb
- test
agent主要用于存放agent及其内部组件有关的代码,包括:amp_agent.sv、amp_agent_config.sv、amp_driver.sv、amp_interface.sv、amp_monitor.sv、amp_seq_item.sv、amp_sequencer.sv。
C_model用于存放和C有关的代码,这里暂时先用不到。
rtl用于存放设计代码,由于该项目的设计代码非常简单,因此只有一个子文件夹amplifier,里面有amplifier.v和param_def.v。
sim用于存放脚本文件,之后会用到。
uvm_tb用于和验证环境有关的其他代码。将agent的部分独立出去其实也能一窥其重要性。其子文件夹中:
- env含有amp_env.sv、amp_env_refm.sv、amp_env_scb.sv;
- sequence含有amp_seq_idle.sv、amp_seq_set_scaler.sv、amp_seq_set_base_number.sv、amp_case1_seq.sv;
- tb含有amp_tb.sv和basic_tb;
- test含有amp_base_test.sv和amp_testcase1.sv
三、添加package
在Windows中,tb文件是通过amp_pkg.svh对每个文件进行include实现索引的。这种方式非常不便于管理。更加正规的方式是使用package实现打包功能。agent文件夹下创建一个名为amp_agent_pkg.sv的文件,其内部代码如下:
点击查看代码
`ifndef AMP_AGENT_PKG_SV
`define AMP_AGENT_PKG_SV
package amp_agent_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "amp_seq_item.sv"
`include "amp_sequencer.sv"
`include "amp_driver.sv"
`include "amp_monitor.sv"
`include "amp_agent_config.sv"
`include "amp_agent.sv"
endpackage : amp_agent_pkg
`endif // AMP_AGENT_PKG_SV
可以看到,package中也主要以导入文件为主。但由于仅导入当前文件夹下的文件,因此管理上更加方便。注意到interface并未包含在其中,这是因为interface在之后会作为单独的文件进行导入。
env中的package为amp_env_pkg.sv,由于需要用到agent中的内容,因此导入agent的包。
点击查看代码
`ifndef AMP_ENV_PKG_SV
`define AMP_ENV_PKG_SV
package amp_env_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
import amp_agent_pkg::*;
`include "amp_env_refm.sv"
`include "amp_env_scb.sv"
`include "amp_env.sv"
endpackage : amp_env_pkg
`endif // AMP_ENV_PKG_SV
sequence中的package为amp_seq_pkg.sv,内容如下:
点击查看代码
`ifndef AMP_SEQ_PKG_SV
`define AMP_SEQ_PKG_SV
package amp_seq_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
import amp_agent_pkg::*;
`include "amp_seq_set_scaler.sv"
`include "amp_seq_set_base_number.sv"
`include "amp_seq_idle.sv"
`include "amp_case1_seq.sv"
endpackage : amp_seq_pkg
`endif // AMP_SEQ_PKG_SV
test文件下的package为amp_test_pkg.sv,内容如下:
点击查看代码
`ifndef AMP_TEST_PKG_SV
`define AMP_TEST_PKG_SV
package amp_test_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
import amp_agent_pkg::*;
import amp_env_pkg::*;
import amp_seq_pkg::*;
`include "amp_base_test.sv"
`include "amp_testcase1.sv"
endpackage : amp_test_pkg
`endif // AMP_TEST_PKG_SV
至此,所有的package编写完成。
四、编写Makefile
4.1 简单的编写
在之前的Questa Sim中,我们使用do文件大幅简化了仿真过程。在这里,也可以使用脚本文件来简化仿真过程。这个脚本就是大名鼎鼎的makefile。
makefile的原理和语法这里不做过多介绍。创建一个名为Makefile的文件,并在其中写入如下代码:
点击查看代码
export FSDB_NAME=amp
SEED=1
VERB=UVM_LOW
TEST_NAME=amp_testcase1
all: comp sim
comp:
vcs -sverilog -full64 +v2k -debug_all -timescale=1ns/1ps -ntb_opts uvm-1.1 -f filelist.f -l comp.log
sim:
./simv +ntb_random_seed=${SEED} +UVM_VERBOSITY=${VERB} +UVM_TESTNAME=${TEST_NAME} \
-ucli -i dump_fsdb_vcs.tcl \
+fsdb+autoflush \
-l sim.log
verdi:
verdi -ssf amp.fsdb -f filelist.f &
clean:
rm -rf *.log *.vdb *simv* *.h *.key *.fsdb *.conf *.rc
rm -rf cg_report csrc vdCovLog verdiLog
这里简要说明一下该makefile脚本:
- FSDB_NAME:该变量供外部tcl脚本使用
- filelist.f:文件列表,包含有所有要编译的文件
- dump_fsdb_vcs.tcl:tcl脚本,用来保存仿真波形
各个选项的意义都可以百度查到。事实上,VCS和Verdi等的资料比Questa Sim多多了,这也是我决定将环境迁移过来的原因之一。
4.2 filelist.f
回到makefile脚本上来。整个脚本中,最关键的地方在于这个filelist.f,其中包含了所有的package和相对路径。+incdir+能够告知VCS,当其需要文件查找时,到哪个文件夹下查询。
点击查看代码
../rtl/amplifier/amplifier.v
+incdir+../rtl/amplifier
../agent/amp_interface.sv
../agent/amp_agent_pkg.sv
+incdir+../agent
../uvm_tb/sequence/amp_seq_pkg.sv
+incdir+../uvm_tb/sequence
../uvm_tb/env/amp_env_pkg.sv
+incdir+../uvm_tb/env
../uvm_tb/test/amp_test_pkg.sv
+incdir+../uvm_tb/test
../uvm_tb/tb/amp_tb.sv
可以看到,使用package加filelist.f的方式,使得整个项目文件更加容易管理,减少了出错的可能。
这里提一下dump_fsdb_vcs.tcl,该tcl文件用于控制保存波形的相关操作,其内容比较简单:
点击查看代码
global env
fsdbDumpfile "$env(FSDB_NAME).fsdb"
fsdbDumpvars 0 "amp_tb"
run
exit
这个FSDB_NAME就是在Makefile中导出的变量,其和.fsdb字段共同组成了一个名为amp.fsdb的文件,该文件保存有仿真后得到的波形信息。
4.3 使用Makefile
在进行仿真之前,需要先对amp_tb.sv进行简单的修改。
点击查看代码
module amp_tb;
import uvm_pkg::*;
`include "uvm_macros.svh"
import amp_test_pkg::*;
logic clk, rst_n;
amp_interface itf(.clk_i(clk), .rstn_i(rst_n));
amplifier dut(
.clk_i (clk) ,
.rstn_i (rst_n) ,
.wr_en_i (itf.wr_en_i) ,
.set_scaler_i (itf.set_scaler_i) ,
.wr_data_i (itf.wr_data_i) ,
.rd_val_o (itf.rd_val_o) ,
.rd_data_o (itf.rd_data_o) ,
.scaler_o (itf.scaler_o)
);
initial begin
clk = 0;
rst_n = 0;
repeat(10) begin
#1ns clk = ~clk;
end
rst_n = 1;
forever begin
#1ns clk = ~clk;
end
end
initial begin
uvm_config_db #(virtual amp_interface)::set(null, "uvm_test_top.env.i_agt", "vif", itf);
uvm_config_db #(virtual amp_interface)::set(null, "uvm_test_top.env.o_agt", "vif", itf);
end
initial begin
run_test();
end
endmodule : amp_tb
比较重要的修改就是去掉了修改冗余度的语句,并且run_test()中没有给任何参数,因为这些参数都可以在Makefile中进行设置。
以上过程都完成之后,就能着手仿真了。
在主界面输入命令make all,之后将以UVM_LOW的冗余度编译运行amp_testcase1这一测试用例。运行完成后,当前路径下出现两个分别命名为comp.log和sim.log的文件。使用vim comp.log打开comp.log,可以看到详细的编译情况;而使用vim sim.log打开sim.log,则可以看到详细的仿真情况。可以看出,其仿真情况与Questa Sim中的日志相同。

使用makefile的另一个好处在于:只需通过改变上述变量的内容,就能灵活地进行仿真。将VERB的值改为UVM_FULL、将TEST_NAME的值改为amp_base_test,之后再次执行命令make sim,会发现此时执行的测试用例变成了amp_base_test,可以再次打开sim.log文件,发现其内容已经发生了完全的改变。当然,并没有因为更改了测试用例就需要重新编译,这对于大型项目的测试简直是福音。
五、使用DPI-C
在Ubuntu+VCS的环境下,使用DPI-C非常方便。首先是在C_model文件夹下创建两个文件:c_amplifier.c和cpp_amplifier.cpp,两个文件的内容分别如下:
c_amplifier.c:
点击查看代码
#include <stdio.h>
int amplifier(int base_number, int scaler) {
return amplifier_by_cpp(base_number,scaler);
}
cpp_amplifier.cpp:
点击查看代码
#include <iostream>
#ifdef __cplusplus
extern "C" {
#endif
class c_cpp{
public:
c_cpp(){};
~c_cpp(){};
int calculate(int base_number, int scaler);
};
int amplifier_by_cpp(int base_number, int scaler);
#ifdef __cplusplus
}
#endif
int c_cpp::calculate(int base_number, int scaler){
using namespace std;
cout<<"calculated by cpp"<<endl;
return base_number*scaler;
};
int amplifier_by_cpp(int base_number, int scaler){
c_cpp c_inst;
return c_inst.calculate(base_number,scaler);
};
然后在filelist.f中加入两个文件:
点击查看代码
../C_model/c_amplifier.c
../C_model/cpp_amplifier.cpp
之后修改amp_env_refm.sv,主要是加入DPI-C接口,并在main_phase()中使用该接口。
点击查看代码
`ifndef AMP_ENV_REFM_SV
`define AMP_ENV_REFM_SV
import "DPI-C" context function int amplifier(input int base_number, int scaler);
class amp_env_refm extends uvm_component;
`uvm_component_utils(amp_env_refm)
uvm_blocking_get_port #(amp_seq_item) gp;
uvm_analysis_port #(amp_seq_item) ap;
amp_seq_item t;
amp_seq_item scb_t;
int res;
extern function new(string name = "amp_env_refm", uvm_component parent = null);
extern function void build_phase(uvm_phase phase);
extern task main_phase(uvm_phase phase);
endclass : amp_env_refm
function amp_env_refm::new(string name = "amp_env_refm", uvm_component parent = null);
super.new(name, parent);
`uvm_info(get_type_name(), "created", UVM_LOW)
endfunction : new
function void amp_env_refm::build_phase(uvm_phase phase);
super.build_phase(phase);
gp = new("gp", this);
ap = new("ap", this);
endfunction : build_phase
task amp_env_refm::main_phase(uvm_phase phase);
super.main_phase(phase);
while(1) begin
t = new("t");
scb_t = new("scb_t");
gp.get(t);
scb_t.copy(t);
res = amplifier(t.base_number, t.rd_scaler); // use DPI-C
scb_t.rd_data = res;
ap.write(scb_t);
end
endtask : main_phase
`endif // AMP_ENV_REFM_SV
最后,重新编译并仿真,可以从输出结果中看到确实调用了cpp文件。

至此,整个项目完成。
本文来自博客园,作者:TooyamaYuuouji,转载请注明原文链接:https://www.cnblogs.com/Lexington-CV2/articles/16653562.html
浙公网安备 33010602011771号