前言
用来入门的UVM项目也太难找了!!!
这是一个从其他博主那参考来的项目,来源请参考我的这篇随笔:https://www.cnblogs.com/Lexington-CV2/p/16597128.html
话不多说,直接从现在开始吧。
一、项目简介
该项目的DUT是一个放大器,作用是将输入的基数和放大倍数进行相乘,再将结果进行输出。其中,基数base_number为8bit,放大倍数scaler为16bit,运算结果res为24bit,输出数据为序号no和res的组合,共计32bit,其中no在输入时获取。
顶层文件接口如图所示:

二、验证计划
2.1 验证平台架构
整体的验证结构如图所示。

参考模型(reference model)和C++之间的连线为虚线,这是因为不是必须使用到C++代码。
绿色的线代表着接口,黑色的箭头线则代表着数据的流向。
2.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。由于没有设置数据,因此输出波形不会有变化,只需要看一下时钟和复位信号的波形,确认待验证代码没有明显的语法错误,编译环境能够正常工作即可。

当然,也可以根据说明文档进行更进一步的检查,比如设定一些简单的激励信号来查看波形和打印信息,就像下面这样:
点击查看代码
`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,得到仿真波形如图所示。

命令行的输出如下:
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,确实是按照设计要求完成了放大操作。
这里需要尤其注意下图中红框的部分。

可以看到,信号set_scaler_i变为高电平,并成功将数据100(图上指的是0064,是以十六进制显示的)写入DUT后,经过了一个时钟周期之后信号rd_val_o才会变为高电平,这一点需要尤其注意。
初步检查没问题之后,就可以正式搭建验证环境了。
本文来自博客园,作者:TooyamaYuuouji,转载请注明原文链接:https://www.cnblogs.com/Lexington-CV2/articles/16597207.html
浙公网安备 33010602011771号