TooyamaYuuouji

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

前言

用来入门的UVM项目也太难找了!!!
这是一个从其他博主那参考来的项目,来源请参考我的这篇随笔:https://www.cnblogs.com/Lexington-CV2/p/16597128.html
话不多说,直接从现在开始吧。

一、项目简介

该项目的DUT是一个放大器,作用是将输入的基数和放大倍数进行相乘,再将结果进行输出。其中,基数base_number为8bit,放大倍数scaler为16bit,运算结果res为24bit,输出数据为序号nores的组合,共计32bit,其中no在输入时获取。

顶层文件接口如图所示:

image

二、验证计划

2.1 验证平台架构

整体的验证结构如图所示。

image

参考模型(reference model)和C++之间的连线为虚线,这是因为不是必须使用到C++代码。

绿色的线代表着接口,黑色的箭头线则代表着数据的流向。

2.2 验证功能点

  1. 数据的读写操作
  2. 基数与放大倍数的乘积结果检验

2.3 验证策略

由于该DUT十分简单,因此直接采用黑盒验证。

三、验证工具

  • 验证语言:System Verilog
  • 验证方法:UVM
  • 验证平台:
    • 前半部分:Windows10 家庭中文版、Questa Sim-64 10.6c
    • 后半部分:Ubuntu18.04、VCS O-2018.09、Verdi O-2018.09

使用两个环境,是因为后续某些操作在Linux上更加方便。

四、文件层次

由于我没接触过真实的工程类项目,因此对文件层次的规范了解不多。在这里,我以src作为存放所有代码的主文件夹,src的父目录作为总的工程文件夹,则整个文件目录结构如下:

  • src
    • DUT
    • agent
    • env
    • sim
    • tb
    • tests
  • work
  • my_amplifier.mpf

work是Questa Sim自动创建的工程文件夹。my_amplifier.mpf则是该工程的数据文件,从这个文件名也可以看出,我将该工程命名为my_amplifier。
src则是代码文件夹,其中:

  • DUT用于存放待验证的设计代码;
  • agent用于存放组件agent以及其内部组件的相关代码,与激励有关的代码也存放至此;
  • env含有scoreboard、reference model等与env同一层次结构的组件代码;
  • sim为仿真用脚本文件所在的文件夹;
  • tb内放置有顶层文件;
  • tests包含有所有的testcase文件。

五、初步检查

在拿到DUT文件后,首先应该写一个简单的tb文件,用来初步检查DUT是否能够正常运行。这个tb文件非常简单,只需要生成时钟信号和复位信号的时序,并根据顶层接口表等文件,创建变量连接好DUT即可。
这个tb文件被命名为basic_tb.sv,放在tb文件夹下。

点击查看代码
`timescale 1ns/1ps
`include "../DUT/param_def.v"

parameter T = 10;

module basic_tb;
    bit                         clk_i, rstn_i;
    
    logic                       wr_en_i;
    logic                       set_scaler_i;
    logic [`WR_DATA_WIDTH-1:0]  wr_data_i;
    
    logic                       rd_val_o;
    logic [`RD_DATA_WIDTH-1:0]  rd_data_o;
    logic [`SCALER_WIDTH-1:0]   scaler_o;

    amplifier dut(
        .clk_i          (clk_i)         ,
        .rstn_i         (rstn_i)        ,
        .wr_en_i        (wr_en_i)       ,
        .set_scaler_i   (set_scaler_i)  ,
        .wr_data_i      (wr_data_i)     ,
        .rd_val_o       (rd_val_o)      ,
        .rd_data_o      (rd_data_o)     ,
        .scaler_o       (scaler_o)
    );

    initial begin
        rstn_i <= 1'b0;
        #(10*T);
        rstn_i <= 1'b1;
    end

    initial begin
        clk_i <= 1'b0;
        forever begin
            #(T/2) clk_i <= ~clk_i;
        end
    end
endmodule : basic_tb

编译后仿真200ns。由于没有设置数据,因此输出波形不会有变化,只需要看一下时钟和复位信号的波形,确认待验证代码没有明显的语法错误,编译环境能够正常工作即可。

image

当然,也可以根据说明文档进行更进一步的检查,比如设定一些简单的激励信号来查看波形和打印信息,就像下面这样:

点击查看代码
`timescale 1ns/1ps
`include "../DUT/param_def.v"

parameter T = 10;

module basic_tb;
    bit                         clk_i, rstn_i;
    
    logic                       wr_en_i;
    logic                       set_scaler_i;
    logic [`WR_DATA_WIDTH-1:0]  wr_data_i;
    
    logic                       rd_val_o;
    logic [`RD_DATA_WIDTH-1:0]  rd_data_o;
    logic [`SCALER_WIDTH-1:0]   scaler_o;

    amplifier dut(
        .clk_i          (clk_i)         ,
        .rstn_i         (rstn_i)        ,
        .wr_en_i        (wr_en_i)       ,
        .set_scaler_i   (set_scaler_i)  ,
        .wr_data_i      (wr_data_i)     ,
        .rd_val_o       (rd_val_o)      ,
        .rd_data_o      (rd_data_o)     ,
        .scaler_o       (scaler_o)
    );

    initial begin
        rstn_i <= 1'b0;
        #(10*T);
        rstn_i <= 1'b1;
    end

    initial begin
        clk_i <= 1'b0;
        forever begin
            #(T/2) clk_i <= ~clk_i;
        end
    end

    // 简单的检查
    initial begin
        wait(rstn_i);   // 等待复位结束

        #T; // 是否有延时会对打印结果产生影响,这也说明了时序图的重要性
        $display("====prepare...====");
        wr_en_i         <= 1'b0;
        set_scaler_i    <= 1'b0;
        wr_data_i       <= 16'd0;
        
        #T;
        $display("====input scaler====");
        wr_en_i         <= 1'b1;
        set_scaler_i    <= 1'b1;
        wr_data_i       <= 16'd100;

        #T;
        $display("scaler = %d\n", wr_data_i);

        $display("rd_val_o = %d", rd_val_o);
        $display("rd_data_o = %d", rd_data_o);
        $display("scaler_o = %d", scaler_o);

        #T;
        wr_en_i         <= 1'b1;
        set_scaler_i    <= 1'b0;
        wr_data_i       <= {8'd5, 8'd25};

        #T;
        $display("====input base number====");
        $display("no = %d", wr_data_i[15:8]);
        $display("base number = %d\n", wr_data_i[7:0]);

        $display("rd_val_o = %d", rd_val_o);
        $display("rd_data_o(no) = %d", rd_data_o[31:24]);
        $display("rd_data_o(res) = %d", rd_data_o[23:0]);
        $display("scaler_o = %d", scaler_o);
        #T;
        $display("====after a clock cycle====");
        $display("rd_val_o = %d", rd_val_o);
        $display("rd_data_o(no) = %d", rd_data_o[31:24]);
        $display("rd_data_o(res) = %d", rd_data_o[23:0]);
        $display("scaler_o = %d", scaler_o);
        $display("====finish====");
    end
endmodule : basic_tb

同样运行200ns,得到仿真波形如图所示。

image

命令行的输出如下:

VSIM 6> run
# ====prepare...====
# ====input scaler====
# scaler =   100
# 
# rd_val_o = 0
# rd_data_o =          0
# scaler_o =   100
# ====input base number====
# no =   5
# base number =  25
# 
# rd_val_o = 0
# rd_data_o(no) =   0
# rd_data_o(res) =        0
# scaler_o =   100
# ====after a clock cycle====
# rd_val_o = 1
# rd_data_o(no) =   5
# rd_data_o(res) =     2500
# scaler_o =   100
# ====finish====

从代码和波形图可以看出,100*25=2500,确实是按照设计要求完成了放大操作。

这里需要尤其注意下图中红框的部分。

image

可以看到,信号set_scaler_i变为高电平,并成功将数据100(图上指的是0064,是以十六进制显示的)写入DUT后,经过了一个时钟周期之后信号rd_val_o才会变为高电平,这一点需要尤其注意。

初步检查没问题之后,就可以正式搭建验证环境了。

posted on 2022-09-27 14:58  TooyamaYuuouji  阅读(1071)  评论(0)    收藏  举报