TooyamaYuuouji

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

从这里开始,将转战到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.svamp_agent_config.svamp_driver.svamp_interface.svamp_monitor.svamp_seq_item.svamp_sequencer.sv
C_model用于存放和C有关的代码,这里暂时先用不到。
rtl用于存放设计代码,由于该项目的设计代码非常简单,因此只有一个子文件夹amplifier,里面有amplifier.vparam_def.v
sim用于存放脚本文件,之后会用到。
uvm_tb用于和验证环境有关的其他代码。将agent的部分独立出去其实也能一窥其重要性。其子文件夹中:

  • env含有amp_env.svamp_env_refm.svamp_env_scb.sv
  • sequence含有amp_seq_idle.svamp_seq_set_scaler.svamp_seq_set_base_number.svamp_case1_seq.sv
  • tb含有amp_tb.svbasic_tb
  • test含有amp_base_test.svamp_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.logsim.log的文件。使用vim comp.log打开comp.log,可以看到详细的编译情况;而使用vim sim.log打开sim.log,则可以看到详细的仿真情况。可以看出,其仿真情况与Questa Sim中的日志相同。

image

使用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.ccpp_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文件。

image

至此,整个项目完成。

posted on 2022-09-28 18:39  TooyamaYuuouji  阅读(1810)  评论(0)    收藏  举报