AXI协议——非对齐/窄传输 读返回data格式

网上有许多关于AXI非对齐传输和窄传输的介绍,但配图和介绍内容均以wdata为例,缺少slave返回读data的处理办法。

通过查阅arm community论坛发布的axi unaligned transfer相关帖子,确认了非对齐/窄传输时读返回data的格式,本文将就此展开介绍。

首先我们假设总线宽度为64 bit,有这样一片内存区域,其中存储的值如下图所示(对应地址为7-0, 15-8):

窄传输

INCR
我们发送burst length为4 transfers,busrt size为32-bits,burst type为INCR,起始地址为0的窄传输包,则返回的rdata分别为0x89abcdef、0x1234567、0x76543210、0xfedcba98,高32位补0。

非对齐传输

INCR
我们发送burst length为4 transfers,busrt size为32-bits,burst type为INCR,起始地址为3的非对齐传输包,则返回的rdata分别为0x23456789、0x54321001、0xdcba9876、0xfe,高32位补0,最后一笔transfer未使用位补0。

安装

1.1 在win10下安装Anaconda及深度学习框架

(1)https://repo.anaconda.com/archive/ 在官网找到自己需要的版本。

(2)https://docs.anaconda.com/anaconda/install/windows/ 参照官方提供的教程,图形化安装。

(3)修改其包管理镜像为国内源

conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --set show_channel_urls yes

(4)运行Anaconda,创建新的环境并安装包:
① 在导航栏选择“Environments”
② 单击环境选择栏下方“Create”按钮,在弹出页面填入新环境的名字“d2l”,选择依赖的python版本,点击Create则生成新环境“d2l”。
③ Anaconda会自动为我们激活新环境,下一步就是所需的安装深度学习框架。
④ 将筛选条件选择为“Not installed”。
⑤ 在搜索框输入自己需安装的框架,如TENSORFLOW / PYTORCH / MXNET等。
⑥ 勾选需要安装的包。
⑦ 点击右下角“Apply”按钮,在弹出的窗口中,Anaconda会为我们搜索当前安装的框架所依赖的包,点击“Apply”,进入安装。在右下角可见安装进度条。
⑧ 切换筛选条件为“Installed”,即可见安装的包。

1.2 下载 D2L Notebook

(1)以管理员模式打开cmd,进入想要下载的路径,输入如下命令:

mkdir d2l-zh && cd d2l-zh
curl https://zh-v2.d2l.ai/d2l-zh-2.0.0.zip -o d2l-zh.zip

(2)打开文件资源管理器,用已有解压工具解压。解压内容如图:

d2l notebook

(3)cmd中激活d2l环境,将当前路径定位到刚下载的本书代码解压后的目录,并打开Jupyter笔记本,如:

conda activate d2l
cd d2l-zh/tensorflow
jupyter notebook
打开Jupyter笔记本

测试点分解理论

1、前仿验证流程

熟悉SPEC -> 验证策略(BT/IT/ST)-> 测试点分解 -> 验证方案 -> 验证环境 -> 冒烟测试 -> 验证执行 -> 验证报告

2、测试点分解的依据

(1)DUT的spec
(2)标准、协议
(3)其他文档(产品需求、架构文档、算法说明、产品说明书、应用手册等)
(4)通用基本逻辑单元的常规测试点
(5)来自设计工程师的要求(是否有特殊的、对极端的电路测试要求)
(6)来自验证工程师的经验

3、测试点和测试用例的关系

(1)一个测试点不能被分解到被多个测试用例包含(但一个测试点是可以在多个用例中被包含的);
(2)一个测试用例可以包含多个测试点;

4、测试点分类

(1)场景类
(2)功能类
(3)性能类
(4)接口类
(5)异常类
(6)白盒测试点(设计提供)

5、测试点分解方法

  • 等价类划分
    输入值的子集,分为有效等价类和无效等价类
    例如:输入值是正整数且小于16,则有效等价类为0<x<=15,小于0和大于16为无效等价类
  • 边界值分析
    把输入值的边界值作为测试点,则可划分为正常区域,正常边界区域,异常边界区域,异常区域。
    例如:输入值是正整数且小于16,则正常边界区域为1和15,异常边界区域为0和16。
  • 因果图表法
    例如:输入a和b得到结果x,输入c和d得到结果y,列出所有的关系,再对不能同时的输入简化,做出测试点。
  • 正交矩阵法
    对输入的各个条件正交,筛选得出可靠的测试点。
    例如:输入参数A有开启、关闭两种情况,参数B有模式1和模式2两种,参数C位宽选择有1bit,8bit和64bit,利用正交矩阵分解就有12种情况:
    – A开启、B为模式1、C位宽选择1bit;
    – A开启、B为模式1、C位宽选择8bit;
    – A开启、B为模式1、C位宽选择64bit;
    – A开启、B为模式2、C位宽选择1bit;
    – A开启、B为模式2、C位宽选择8bit;
    – A开启、B为模式2、C位宽选择64bit;
    – A关闭、B为模式1、C位宽选择1bit;
    – A关闭、B为模式1、C位宽选择8bit;
    – A关闭、B为模式1、C位宽选择64bit;
    – A关闭、B为模式2、C位宽选择1bit;
    – A关闭、B为模式2、C位宽选择8bit;
    – A关闭、B为模式2、C位宽选择64bit;
  • 随机变量法
    对输入的值进行随机化,一般和等价类法联合使用,在等价区域使用随机化的值。
    例如:输入值是正整数且小于等于1000,有效等价类是0<x<=1000,如果不需要覆盖到1~1000的所有值,有效边界值是1和1000,可分解测试点为:1,2,[3:998],999,1000。
  • 错误推断法
    根据经验和对DUT的理解,假设DUT在特定激励下会发生异常或错误,有目的构建一些测试用例去捕捉这类错误或确定错误不存在。
    例如:可能有些设计人员没考虑全反压的情况,在测试点分解时可增加接口上的反压测试,可通过force一定时间FIFO的full或afull信号,观察逻辑处理情况。
  • 场景分析法
    根据用户的使用场景进行测试点分解。
    例:如下图状态机,初始状态为S0,可经过两条路径到达S3,
    即,路径1:S0-S1-S2-S3-S0;路径2:S0-S1-S3-S0

UVM(三)—— Sequence机制

1. Sequenece机制基础

1.1 概念

控制和产生一系列的激励,并通过sequencer将激励发送给driver。
是一个产生和发送数据的过程,需要消耗仿真时间,要用在task phase中启动。
将激励的产生与驱动分离,提高了测试平台中组件的可重用性,使得事务的产生更加灵活。

1.2 原理

1.3 相关宏

  • uvm_do系列宏
    uvm_do系列的其他七个宏其实都是用uvm_do_on_pri_with宏来实现的。

  • uvm_create与uvm_send
    uvm_create:实例化transaction。
    uvm_send:将处理完的transaction发送出去。
    uvm_send_pri:将transaction交给sequencer时设定优先级。
  • uvm_rand_send系列宏
    前提:transaction已经被分配了空间。
    意义:如果一个transaction占用的内存比较大,那么很可能希望前后两次发送的transaction都使用同一块内存,只是其中的内容可以不同,这样比较节省内存。
  • start_item与finish_item
    必须要先实例化transaction后才可以调用这两个任务。
    可以在transaction实例化后、finish_item调用前对其进行随机化。
    指定transaction优先级,要在调用start_item和finish_item时都要加入优先级参数。不指定默认为-1。
  • pre_do、mid_do与post_do

    pre_do任务的参数用于表明uvm_do宏是在对一个transaction还是在对一个sequence进行操作。
    mid_do和post_do的两个参数是正在操作的sequence或者item的指针,但是其类型是uvm_sequence_item类型。通过cast可以转换成目标类型。
    意义:增加uvm_do系列宏的功能。

1.4 三种启动方式

(1)直接使用start方式启动sequence
(2)配置sequencer中的default_sequence(test中的build_phase完成)
(3)使用`uvm_do系列宏、`uvm_send系列宏、`uvm_rand_send系列宏启动sequence

当一个sequence启动后会自动执行sequence的body任务。
前两种方式除了执行body外,还会自动调用sequence的pre_body与post_body任务。第三种则不会。

1.5 sequencer仲裁算法

算法参数说明
SEQ_ARB_FIFO默认算法。先入先出,不考虑优先级。
SEQ_ARB_WEIGHTED优先执行高权值的sequence
SEQ_ARB_RANDOM随机执行sequence
SEQ_ARB_STRICT_FIFO按照优先级,同一优先级,先入先出
SEQ_ARB_STRICT_RANDOM按照优先级,同一优先级,随机
SEQ_ARB_USER用户自定义。需扩展uvm_sequencer并且重载user_priority_arbitration

1.6 sequence对sequencer的占用或失效

1.6.1 用lock()和grab()排他性占用sequencer
sequence的body()中 使用lock()…unlock()或grab()…ungrab(), 可以让在其中间发送(假设中间的sequence具有最高级)的所有transaction连续地获取sequencer的仲裁,从而连续地发送到driver而不被别的sequence打断。
两者区别——仲裁机制不一样:
(1)lock()会放在仲裁队列的末尾,sequencer将原来就在仲裁队列里的transaction发送完后才执行lock的transaction,一旦lock占用sequencer后,只有unlock后才释放。
(2)grab()会放在仲裁队列的最前面,grab操作比lock操作优先级更高,sequencer会立即执行grab的transaction,同样一旦grab占用sequencer后,只有ungrab后才释放。
(3)grab实时性强,是插队操作。unlock则不是。

1.6.2 sequence的is_revelant函数和wait_for_relevant任务
sequencer在仲裁时, 会查看sequence的is_relevant函数的返回结果, 为1说明此sequence有效并参加仲裁, 否则无效。
可以通过重载is_relevant函数来使sequence失效。
当sequencer将所有有效transaction发送完毕后,它会调用处于无效状态的sequence的wait_for_relevant任务。当wait_for_relevant返回后,sequencer还会继续调用is_relevant函数。
在wait_for_relevant任务中,user一定要将使sequence的无效状态的条件清除。否则系统会进入死循环。

1.7 在sequence中获得my_sequencer中变量的值

假设我们需要在 sequence 中使用 my_sequencer 类型中的 dmac和smac 变量。
两种方法:
(1)通过cast转换将m_sequencer转换成my_sequencer类型,并引用其中的dmac和smac。
(2)使用`uvm_declare_p_sequencer(my_sequencer),会自动将m_sequencer通过cast转换成p_sequencer。这个过程在pre_body()之前就完成了。因此在sequence中可以直接使用成员变量p_sequencer来引用dmac和smac。

2. Virtual Sequence

假设我们有如下图的需求,DUT有许多接口,不同接口需要不同的sequence产生激励,那我们要如何控制不同Sequence的执行顺序呢?

在此,我们引入virtual sequence的概念。Virtual sequence是 具有管理多个sequence,并控制它们之间关系功能 的sequence。

配套的,我们需要使用virtual sequencer来管理多个sequencer。

代码实现如下:

3. Sequence library(了解即可)

3.1 概念

– 一个sequence
– 包含了一组在其内部注册了的sequence类型
– 可通过对其进行配置,创建并执行一系列的sequence/sequence item

3.2 功能

– 其他sequence可以向sequence library注册
– 可根据配置产生并且执行已经在其内部注册过的sequence
– 具有多种内嵌的选择sequence的算法
– 支持用户自定义的sequence选择算法
– 注册方式的多样化

3.3 向sequence library中注册sequence

3.3.1 永久注册
(1)创建sequence library类。
// 从uvm_sequence派生时要指明此sequence library所产生的transaction类型
class simple_seq_library extends uvm_sequence_library#(my_transaction);
function new(string name= "simple_seq_library");
super.new(name);
// 在new函数中要调用init_sequence_library,否则其内部的候选sequence队列就是空的
init_sequence_library();
  endfunction
  `uvm_object_utils(simple_seq_library)
  // 要调用 uvm_sequence_library_utils注册
  `uvm_sequence_library_utils(simple_seq_library);
endclass


(2)创建sequence并注册进simple_seq_library中。
// 第一个是此sequence的名字,第二个是要加入的sequence library的名字
`uvm_add_to_seq_lib(seq0, simple_seq_library)


(3)将simple_seq_library配置给sequencer的default_sequence,以启动sequence lib。

3.3.2 临时添加——使用类配置
(1)创建sequence library类和sequence类。
(2)在test中根据实际需求将sequence添加到sequence library中,simple_seq_library为注册目标的sequence library。
simple_seq_library::add_typewide_sequence(my_sequence::get_type());
(3)将simple_seq_library配置给sequencer的default_sequence,以启动sequence lib。

3.3.3 在某一个单独的sequence library实例中添加sequence
(1)创建sequence library类和sequence类。
(2)在test中声明sequence library句柄,在build_phase中实例化sequence对象,向sequence library中注册sequence。
simple_seq_library m_seqlib;
virtual function void build_phase(uvm_phase phase);
    ...
    m_seqlib = simple_seq_library::type_id::create(“m_seqlib”, this);
    m_seqlib.add_sequence(my_sequence::get_type());
    ...
endfunction

(3)将simple_seq_library配置给sequencer的default_sequence,以启动sequence lib。

3.4 相关参数及配置

selection_mode参数可选配置如下:

参数配置两种方法如下:



代码示例仓库
https://gitee.com/mala_wong/uvm_sequence_test

参考资料:
• 白皮书《UVM实战》第6章 UVM中的sequence
• B站教程 《UVM基础》P32-P37 P52-P57
• CSDN博文
https://blog.csdn.net/weixin_43830240/article/details/111193810
https://blog.csdn.net/weixin_43830240/article/details/111220598?spm=1001.2014.3001.5501

UVM(二)——TLM通信

1. 什么是TLM

TLM是Transaction Level Modeling(事务级建模),为组件之间的通信建立专门的通信信道。

2. TLM原理

3. 常用TLM port/imp/export

portexportimport
putuvm_blocking_put_port#(T);
uvm_nonblocking_put_port#(T);
uvm_put_port#(T);
uvm_blocking_put_export#(T); uvm_nonblocking_put_export#(T);
uvm_put_export#(T);
uvm_blocking_put_imp#(T, IMP); uvm_nonblocking_put_imp#(T, IMP);
uvm_put_imp#(T, IMP);
getuvm_blocking_get_port#(T);
uvm_nonblocking_get_port#(T);
uvm_get_port#(T);
uvm_blocking_get_export#(T); uvm_nonblocking_get_export#(T);
uvm_get_export#(T);
uvm_blocking_get_imp#(T, IMP); uvm_nonblocking_get_imp#(T, IMP);
 uvm_get_imp#(T, IMP);
peekuvm_blocking_peek_port#(T);
uvm_nonblocking_peek_port#(T);
uvm_peek_port#(T);
uvm_blocking_peek_export#(T); uvm_nonblocking_peek_export#(T);
uvm_peek_export#(T);

uvm_blocking_peek_imp#(T, IMP); uvm_nonblocking_peek_imp#(T, IMP);
uvm_peek_imp#(T, IMP);
get peekuvm_blocking_get_peek_port#(T); uvm_nonblocking_get_peek_port#(T);
uvm_get_peek_port#(T);
uvm_blocking_get_peek_export#(T);  uvm_nonblocking_get_peek_export#(T);
uvm_get_peek_export#(T);
uvm_blocking_get_peek_imp#(T, IMP); uvm_nonblocking_get_peek_imp#(T, IMP);
uvm_get_peek_imp#(T, IMP);
transportuvm_blocking_transport_port#(REQ, RSP); uvm_nonblocking_transport_port#(REQ, RSP);
uvm_transport_port#(REQ, RSP);
uvm_blocking_transport_export#(REQ, RSP); uvm_nonblocking_transport_export#(REQ, RSP);
uvm_transport_export#(REQ, RSP);
uvm_blocking_transport_imp#(REQ, RSP, IMP); uvm_nonblocking_transport_imp#(REQ, RSP, IMP); uvm_transport_imp#(REQ, RSP, IMP);

每一个IMP要和一个component相对应。
按照控制流的优先级排序,UVM中 三种端口顺序为:PORT > EXPORT  > IMP。

4. 常用TLM端口使用方法

端口类型实现方法
uvm_put_port #(T)put()
try_put()
can_put()
uvm_blocking_put_port #(T)put()
uvm_nonblocking_put_port #(T)try_put()
can_put()
uvm_get_peek_portget()、try_get()、can_get()
peek()、try_peek()、can_peek()
uvm_blocking_get_peek_portget()
peek()
uvm_nonblocking_get_peek_porttry_get()、can_get()
try_peek()、can_peek()
uvm_transport_porttransport()
nb_transport()
uvm_blocking_transport_porttransport()
uvm_nonblocking_transport_portnb_transport()

上表中省略了get、peek port,与get_peek_port类似。

put() 是一个task,可能会阻塞当前进程直到transaction传输成功。
try_put() 是一个function,不会阻塞当前进程,不管transaction传输是否完成都会返回。
can_put() 是一个function,不会阻塞当前进程,不发生transaction传输,只是检验对方是否准备好了接受。
get()、peek()与put()类似;try_get()、try_peek()与try_put()类似;can_get()、can_peek()与can_put()类似。

对于analysis_port和analysis_export来说,没有阻塞和非阻塞的概念。只有一种操作:write。

从功能上来看 export和port的功能是一样的,通常export作为中间端口使用。
在import中重载实现表格中的对应方法,并通过发送方的 port名.方法 调用。
因此只有import才能作为连接关系的终点。

4. FIFO

peek端口与get相似,其数据流、控制流都相似,唯一的区别在于当get任务被调用时,FIFO内部缓存中会少一个transaction,而 peek被调用时,FIFO会把transaction复制一份发送出去,其内部缓存中的transaction数量并不会减少。

FIFO的类型有两种,一种是上图中的uvm_tlm_analysis_fifo,另外一种是uvm_tlm_fifo。这两者的唯一差别在于前者有一个 analysis_export端口,并且有一个write函数,而后者没有。

fifo的调试函数

函数名用途
used()用于查询FIFO缓存中有多少transaction。
is_empty()用于判断当前FIFO缓存是否为空。
is_full()用于判断当前FIFO缓存是否已经满了。
flush()用于清空FIFO的缓存。
new
fifo本质上是个component,前两个参数与componet new函数中参数一致。第三个参数用于设定FIFO缓存的上限,默认为1。若要把缓存设置为无限大,则将传入的size参数设置为0。
size()返回FIFO缓存的上限。

一般来说,使用 uvm_analysis_port、uvm_tlm_analysis_fifo、uvm_analysis_imp 即可。

UVM(一)—— field automation、config_db、factory机制

1. field_automation机制

1.1 介绍
利用`uvm_field_* 宏,将类中的成员变量注册到UVM中,UVM可自动为其实现compare、copy、print、pack/unpack等方法,不需要自己去定义。解决 逐字段对transaction进行操作 的问题,极大地简化了验证平台的搭建,提高了效率。

1.2 用法
使用uvm_object_utils_begin和uvm_object_utils_end来实现my_transaction的factory注册,在这两个宏中间,使用uvm_field宏注册所有字段。uvm_field系列宏随着transaction成员变量的不同而不同。

1.3 相关宏

简单的uvm_field系列宏
`define uvm_field_int(ARG,FLAG)
`define uvm_field_real(ARG,FLAG)
`define uvm_field_enum(T,ARG,FLAG)
`define uvm_field_object(ARG,FLAG)
`define uvm_field_event(ARG,FLAG)
`define uvm_field_string(ARG,FLAG)
动态数组:
`define uvm_field_array_enum(ARG,FLAG)
`define uvm_field_array_int(ARG,FLAG)
`define uvm_field_array_object(ARG,FLAG)
`define uvm_field_array_string(ARG,FLAG)
静态数组:
`define uvm_field_sarray_int(ARG,FLAG)
`define uvm_field_sarray_enum(ARG,FLAG)
`define uvm_field_sarray_object(ARG,FLAG)
`define uvm_field_sarray_string(ARG,FLAG)
队列:
`define uvm_field_queue_enum(ARG,FLAG)
`define uvm_field_queue_int(ARG,FLAG)
`define uvm_field_queue_object(ARG,FLAG)
`define uvm_field_queue_string(ARG,FLAG)
联合数组:(第一个类型是存储数据类型,第二个类型是索引类型)
`define uvm_field_aa_int_string(ARG, FLAG)
`define uvm_field_aa_string_string(ARG, FLAG)
`define uvm_field_aa_object_string(ARG, FLAG)
`define uvm_field_aa_int_int(ARG, FLAG)
`define uvm_field_aa_int_int_unsigned(ARG, FLAG)
`define uvm_field_aa_int_integer(ARG, FLAG)
`define uvm_field_aa_int_integer_unsigned(ARG, FLAG)
`define uvm_field_aa_int_byte(ARG, FLAG)
`define uvm_field_aa_int_byte_unsigned(ARG, FLAG)
`define uvm_field_aa_int_shortint(ARG, FLAG)
`define uvm_field_aa_int_shortint_unsigned(ARG, FLAG)
`define uvm_field_aa_int_longint(ARG, FLAG)
`define uvm_field_aa_int_longint_unsigned(ARG, FLAG)
`define uvm_field_aa_string_int(ARG, FLAG)
`define uvm_field_aa_object_int(ARG, FLAG)

2. config_db机制

2.1 介绍
用来传递参数,一般有以下三种应用场景:
(1)传递virtual interface到环境中。
(2)设置单一变量值,例如int、string、enum等。
(3)传递配置对象到环境。

2.2 用法
set函数是寄信,而get函数是收信。如在某个测试用例的build_phase中可以使用如下方式寄信:

uvm_config_db#(int)::set(this,"env.i_agt.drv","pre_num",100);

其中第一个和第二个参数联合起来组成目标路径,与此路径符合的目标才能收信。第一个参数必须是一个uvm_component实例的指针,第二个参数是相对此实例的路径。第三个参数表示一个记号,用以说明这个值是传给目标中的哪个成员的,第四个参数是要设置的值

在driver中的build_phase使用如下方式收信:

uvm_config_db#(int)::get(this,"","pre num",pre num);

get函数中的第一个参数和第二个参数联合起来组成路径。第一个参数也必须是一个uvm_component实例的指针,第二个参数是相对此实例的路径。一般的,如果第一个参数被设置为this,那么第二个参数可以是一个空的字符串。第三个参数就是set函数中的第三个参数,这两个参必须严格匹配,第四个参数则是要设置的变量

2.3 使用实例
2.3.1 interface传递
在实现接口传递的过程中需要注意:
(1)在传递过程中的类型应当为virtual interface,即实际接口的句柄。
(2)接口传递应该发生在run_test()之前。这保证了在进入build phase之前,virtual interface已经被传递到uvm_config_db中。

interface intf1; 
    logic enable=0; 
endinterface 

class compl extends uvm component;
    `uvm_component_utils(comp1)
    virtual intf1 vif;

    function void build_phase(uvm phase phase); 
        if(!uvm config db#(virtual intf1):: get(this,"","vif", vif)) begin 
            uvm_error("GETVIF","no virtual interface is assigned")
        end 
        `uvm_info("SETVAL",$sformatf("vif. enable is %b before set", vif. enable), UVM LOW)
        vif.enable=1;
        `uvm info("SETVAL",$sformatf("vif. enable is b after set", vif. enable), UVM LOW)
    endfunction 
endclass

class testl extends uvm_test; 
    `uvm_component_utils(test1)
    comp1(c1) 

endclass 

intf1 intf(); 
initial begin 
    uvm_config_db#(virtual intf1)::set(uvm root::get(),"uvm test top. cl","vif", intf); 
    run test("test1"); 
end

//输出结果:
UVM_INFO @ 0:reporter [RNTST] Running test test1...
UVM_INFO @ 0:uvm_test_top.cl [SETVAL] vif.enable is 0 before set 
UVM_INFO @ 0:uvm_test_top.cl [SETVAL] vif.enable is 1 after set

2.3.2 变量传递
在各个test中, 可以在build phase对底层组件的变量加以配置, 进而在环境例化之前完成配置, 使得环境可以按照预期运行。
代码与2.3.1类似。

2.3.3 object传递
在test配置中,需要配置的参数不只是数量多,而且可能还分属于不同的组件,如果每个变量单独传递,容易出错,还不能复用。因此我们可以将每个组件中的变量加以整合,首先放置到一个uvm_object中,再对中心化的配置对象进行传递,会更有利于整体环境的修改维护。

class configl extends uvm object;
    int val1=1; 
    int str1="null"; 
    uvm_object_uti1s(config1)
    ...
endclass 

class comp1 extends uvm_component;
    `uvm_component_uti1s(comp1)
    configl cfg; 
    ...
    function void build_phase(uvm phase phase); 
        uvm_object_tmp; 
        uvm_config_db#(uvm_object):: get(this,"","cfg", tmp); 
        void'($cast(cfg, tmp)); 
        uvm_info("SETVAL",$sformatf("cfg.vall is %d after get", cfg. val1), UVM_LOW)
        uvm_info("SETVAL",$sformatf("cfg.str1 is %s after get", cfg. str1), UVM_LOW)
    endfunction
endclass

class test1 extends uvm_test;
    `uvm component utils(test1)
    comp1 c1,c2;
    config1 cfgl,cfg2; 
    ...
    function void build_phase(uvm_phase phase); 
        cfg1=config1::type id::create("cfg1"); 
        cfg2=configl::type_id::create("cfg2"); 
        cfg1.val1=30; 
        Cfg1.strl="c1"; 
        Cfg2.val1=50; 
        cfg2.strl="c2"; 
        uvm_config_db#(uvm_object):set(this,"cl","cfg", cfg1); 
        uvm_config_db#(uvm object):set(this,"c2","cfg", cfg2); 
        c1=comp1::type id::create("c1", this);
        c2=comp1::type_id::create("c2", this); 
    endfunction 
endclass

3. factory机制

3.1 介绍
工厂模式的主要解决的问题是,将原来分布在各个地方的对象创建过程单独抽离出来,交给工厂类负责创建。其他地方想要使用对象直接找工厂(即调用工厂的方法)获取对象。
对象由工厂生产是利用工厂生产模具可灵活替代的好处,在不修改原有验证环境层次和验证包的同时,实现对环境内部组件类型或者对象的覆盖(override,或译成重载)

3.2 用法
3.2.1 定义->注册->构建 缺一不可
component 构建函数
function new (string name = " name", uvm_component parent = null)
创建对象
comp_type::type_id::create(string name,uvm_component parent);

object 构建函数
function new (string name = “name”)
创建对象
object_type::type_id::create(string name);

class comp1 extends uvm_component;//1.定义
    `uvm_component_utils(comp1)//2.注册
    //3.构建函数
    function new (string name = "comp1", uvm_component parent = null);
        super.new(name,parent);
    endfunction:new
endclass
class obj1 extends uvm_object;//1.定义
    `uvm_object_utils(obj1)//2.注册
    function new(string name = "obj1");//3.构建函数
        super.new(name);
    endfunction:new
endclass

//创建对象
comp1 c1,c2;
obj1 o1,o2;
initial begin
    c1 = new("c1");
    o1 = new("o1");
    //建议使用下面方法创建对象
    c2 = comp1::type_id::create("c2",null);
    o2 = obj1::type_id::create("o2");
end

3.2.2 覆盖方法
覆盖机制可以将其原来所属类型替换为另一个新类型
在覆盖之后,原本用来创建原型类型的请求,将由工厂来创建新的替换类型。
(1)无需修改原始代码,保证了原有代码的封装性
(2)新的替换类型必须与被替换的类型相兼容,否则稍后的句柄赋值将失败,所以使用继承。
(3)做顶层修改时,非常方便

使用create()来创建对象时:
(1)工厂会检查,是否有类型被覆盖,如果是,它会创建一个新类型的对象,如果不是,它会创建一个原有类型的对象

覆盖发生时,可以使用“类型覆盖”或者“实例覆盖”:
(1)类型覆盖(set_type_override())指,UVM层次结构下的所有原有类型都被覆盖类型所替换。
(2)实例覆盖(set_inst_override())指,某些位置中原有类型会被覆盖类型所替换

HDLBits总结

地址:https://hdlbits.01xz.net/

题号题目名称注意点
72Bcdadd4这道题目主要是在要了解BCD码以及理解题目的要求,其实就是4位BCD码的加法器,且1位BCD码加法器模块已提供,只需例化,实现起来非常简单
73-80卡诺图相关题这些题目其实重点不在写代码,还是做题前先整明白题目的卡诺图表达的真值
105countbcd和72题相比,需要自己实现1位BCD码加法器,主要难点是计数和进位的条件
106count clock这道题主要难点同105,但是更复杂,时间是60进位和12进位的,同时还要注意pm12:59:59的下一个时刻是am00:00:00。
119-158状态机题目在开始写代码之前最好都先画个状态图,整理思路。写状态机代码的时候要有一个合理的代码结构,尽量一个always块只实现一个功能,增加代码的可读性。
159-163代码改错题这些题目中的问题其实也是自己写代码的时候容易犯的错误,比如不同位数的数据相与,导致高位数据丢失;例化时参数顺序未对应;wire位宽定义错误。
174-178testbench题要对tb有一个基础的了解,比如时钟信号怎么生成,怎么延时等。

芯片开发流程 & 验证流程

芯片开发流程
此图片的alt属性为空;文件名为image.png

数字芯片验证的基本概念

概念芯片验证就是采用相应的验证语言,验证工具,验证方法,在芯片生产之前确认芯片的设计:
1)是否符合客户的原始需求,
2)是否符合芯片定义的规格,
3)是否发现并更正了所有的缺陷。
目标是缺陷零,不是跑完所有的用例或者覆盖到所有测试点:
(1)BT无缺陷遗漏至IT,
(2)IT无缺陷遗漏至ST,
(3)ST无缺陷遗漏至FPGA/EMU,
(4)整体无缺陷遗漏至Tapeout。
核心是测试点分解,不是跑用例:
SPEC —> TEST FEATURE —> TEST POINT

测试点分为场景类、功能类、性能类、白盒测试点(设计人员提供)、接口类、异常类等维度。
全面、明确、细致,无歧义的将所有验证特性细化为一个个不可分割的小点,每个点明确激励策略、检查策略,采用directtest还是coverage还是assertion确保覆盖。
功能覆盖需要细化覆盖范围,比如典型值、边界值、异常值、cross。测试点也不是一次分解完成,在整个验证过程中会进行多次分解和review,直至完善。

数字芯片验证流程