想查看SystemVerilog和UVM提示和小技巧吗?

20200429 SystemVerilog的静态方法

 

前言

在我的上一篇博客中,你已经学习了如何创建具有静态属性的类。这类变量的作用类似于全局变量,因为无论你创建多少个对象,都仅存在一个副本。这篇展示了如何创建访问那些静态属性的方法。

方法

任何类方法都可以读取和写入这些静态属性,就像动态属性一样。但是,如果将方法声明为静态方法,则可以调用该方法而无需构造对象。以下示例是带有静态方法的Thing类,用于打印Thing对象的数量。即使没有构造Thing对象,也可以调用Thing :: print_count()。

 

class Thing;

int id;           // Dynamic, unique to each object

static int count; // Static, associated with the class

 

static function void print_count();

$display(“Thing::count = %0d.”, count);

endfunction

// The rest of the class

endclass

 

静态方法只能访问静态属性。请记住,静态属性的作用就像全局变量,因为它始终存在。动态属性存储在对象中。 id属性仅存在于单个对象中,因此print_count()无法看到它。

 

这是一个简单的错误处理类,该类跟踪已打印的错误消息数,并在达到最大值时退出。请注意,没有创建ErrorH句柄或对象。复制这段代码,然后在模拟器上尝试一下。

 

class ErrorH;

static int count=0, max_count=10;

 

static function void print(input string s);

$error(s);

if (++count >= max_count) begin

$display(“*** Error max %0d exceeded”, max_count);

$finish(0);

end

endfunction

 

static function void set_max_count(input int m);

max_count = m;

endfunction

endclass

 

initial begin

ErrorH::set_max_count(2); // Limit the number of errors

ErrorH::print(“one”);

ErrorH::print(“two – should end sim”);

ErrorH::print(“three – should not print”);

end

 

更多小技巧

uvm_config_db类是基于静态方法构建的,因此你需要使用以下语法进行调用。

 

uvm_config_db::set(…)

uvm_config_db::get(…)

 

如果没有静态方法,则必须构造一个数据库(DB)对象,并将其句柄传递到测试平台的每个角落。这违反了简化配置信息共享的整个目标。

 

这部分没有特意展示。期待在即将发布的博文中发现更多详细信息

 

20200421 具有静态属性的SystemVerilog类

 

前言

与传统的面向过程的编程相反,使用面向对象(Object Oriented Programming,OOP)的编程来创建测试平台的优点之一是,你的数据和代码包含在一个类中,从而减少了对全局变量的需求。这些很有用,但是你需要谨慎使用它们以限制访问它们的方式,从而避免数据损坏。另外,你需要为全局变量创建唯一的名称,以避免冲突。是否有一种OOP方法可以使一个变量可以跨多个类访问并解决命名问题?

对象计数

当你调试带有多个对象的代码时,为每个对象使用唯一的名称或ID十分方便,因此你可以轻松地在系统中跟踪它们。那该如何自动设置ID呢?你可以用一个全局变量来计这种类型的对象的数量,但是你试图避免使用全局变量。 OOP解决方案是一个静态变量,其中的存储与类声明相关联,而不是与动态构造的多个对象相关联。

 

class Thing;

int id;           // Dynamic, unique to each object

static int count; // Static, associated with the class

function new();

id = count++;   // Initialize ID, update count

endfunction

// The rest of the class

endclass

 

每次构造新对象时,其ID都会获取当前计数,然后计数增加。 count属性保存创建的对象数。由于此属性是静态的,因此即使没有创建任何对象,该属性也存在。下面显示的冒号-语法称为作用域解析运算符,它表示要在名称空间Thing中查找名称计数。

 

Thing t0, t1, t2;

initial begin

// Print the count, before any objects constructed

$display(“count=%0d. before”, Thing::count);

t0 = new();

 

// Print count after the first object constructed

$display(“count=%0d. after”, Thing::count);

t1 = new();

t2 = new();

end

 

下图展示了刚才创建对象的过程。静态计数属性与该类相关联,并且仅存在一个副本。句柄t0,t1和t2指向单独的对象,每个对象都有单独的存储空间和唯一的id值。

局部全局变量

啊哈-你刚刚创建了一个“全局”变量count,该变量保存到目前为止创建的Thing对象的数量,但对于Thing类而言是“本地”的。用OOP的术语,你可以说该属性在Thing类的名称空间中。

每个类都有其自己的名称空间,因此Thing类中称为“ count”的属性不会与另一个类中的“count”冲突。每个类都可以有自己的计数变量,如果用全局变量来完成,则不会造成任何混乱。

20200416 SystemVerilog参数化的类

SystemVerilog允许你创建参数化的模块和类。这使它们更加灵活,并且能够处理多种数据类型,而不仅仅是一种。此概念已在UVM中广泛使用,尤其是利用uvm_config_db来配置数据库。你可以自己动手尝试下面这些示例。

按值参数化

让我们从一个简单类——比特位向量开始。该类具有向量宽度的参数。 (良好的编程习惯是始终为你的参数设置默认值。)

 

class Vector #(parameter WIDTH=1);

bit [WIDTH-1:0] data;

endclass

 

现在,你可以为具有各种宽度的向量的类声明句柄。

 

Vector v1;                 // Default: data width=1

Vector #(8) v8;            // 8-bit data

Vector #(.WIDTH(16)) v16;  // 16-bit data

initial begin

v1 = new();

$display(“v1 “, $typename(v1.data)); // v1 bit[0:0]

v8 = new();

$display(“v8 “, $typename(v8.data)); // v8 bit[7:0]

end

 

 

每次你指定一个值时,实际上就是在创建一个新类。因此,你可以想象上面包含v8的那行实际上创建了以下类声明。

 

class Vector__8;

bit [8-1:0] data;

endclass

 

v1,v8和v16句柄类型不兼容,因为它们分别用于单独的类Vector_1,Vector_8和Vector_16。即使使用$ cast(),也无法在这些句柄之间进行赋值。

 

按类型参数化

假设你的老板要求你在SystemVerilog中为堆栈编写公用类。你可能会想到这样的事情。

 

class Stack;

int items[64], idx=0;

function void push(input int val);

items[idx++] = val;

endfunction

 

function int pop();

return items[idx–];

endfunction

endclass

 

你写的类如此受欢迎,以至于你项目中的其他人要求你提供字节堆栈,实数值堆栈等等。无需编写几十个新类,只需修改你的旧类即可。 push()和pop()的代码保持不变,只需适用于其他类型。

 

class Stack #(parameter type T=int);

T items[64], idx=0;

function void push(input T val); …

function T pop(); …

endclass

 

Stack              int_stack;    // Default: Stack of ints

Stack #(bit[7:0]) byte_stack;  // Stack of 8-bit values

Stack #(real)      real_stack;  // Stack of real values

 

再者,这三个声明实际上创建了三个新类。因此,这些句柄类型不兼容。

 

当然,你本可以告诉老板使用System Verilog的队列数据类型,但是这样做的还有什么乐趣呢?

更多小技巧

类标题中的关键字“parameter”是可选的。

参数名称采用大写以表示它不是变量。

 

20200401 给UVM新用户的小贴士(又名:我在类中遗漏了什么)

 

当我第一次学习UVM时,有很多事情让我感到困惑。在修完UVM课程之后,还有什么模糊的地方?

这里有一个图表,展示不同测试平台层次,其中橘色表示事务从测试级序列流入代理项。

 

  • 序列发生器与序列。这个“ 发生器”使很多人感到困惑。如果你不熟悉UVM,请记住:
    • 发生器基本上是一个智能管道,可将事务从测试级传输到驱动程序。 (是的,它可以做更多……)
    • 多个事务被称为序列。一次传输不会发现很多错误,但是大量的传输可能很危险!

 

  • 为什么要在一个任务中生成多个事务?
    • 好的,我正在生成事务。我为什么不只是将它们放入数组呢?
    • 测试平台的第四个维度是时间!如果我只是制作了一系列事务句柄,那要如何添加延迟?如果我有两个序列并排运行,传输到两个不同的接口中,如何能使它们同步?
    • 每个序列都需要在body()任务中运行,因此我可以完成所有这些操作。 (牢记,function中不能有任何延迟。)
    • 激励的反馈呢?事务句柄的数组是静态的。如果要发送事务A,检查结果并在成功时发送B,或在失败时发送C,该怎么办?或者如何从读取的事务中获取值并将其写入新位置?这在SystemVerilog的任务中很容易,但是对于句柄数组却很难。
  • 我的UVM类构造函数什么时候该有一个还是两个参数?这是因为UVM有两种主要类型的类。
    • 监测器(monitor)和驱动(driver)等组件(component)是测试平台层次结构的一部分,即上图中用框表示的部分。
      • 每个测试有一个环境,这个环境包含一个代理(agent),该代理由监测器(monitor),驱动(driver)和序列生成器(sequencer)构成。创建组件时,它需要知道其名称和上一级。因此它们的new()函数必须具有这两个参数。
    • 事物或序列项,即上图的橙色圆圈。这些对象(object)是在测试级别创建的,并发送给代理,或由监视器创建并发送到记分板或由专家进行分析。他们在测试平台上没有固定的位置。这就是为什么他们的new()仅具有单个name参数的原因。
  • “开始序列”是什么意思?
    • 序列产生事务。那它们去哪儿了?如果它们是UART事务,则很明显,它们将转到UART代理。如果你的设计中有4个UART,该怎么办?然后,您的测试台具有4个UART代理。你的测试项需要选择一个。
    • 代理只是其他组件的容器。因此,一个序列需要连接到驱动。但是,如果两个序列都想发送到单个驱动,该怎么办。啊哈-你需要一个智能连接器,一个序列生成器。这就是为什么你的测试类将序列生成器句柄传递到序列start()任务中的原因。
    • 我更喜欢说“用序列生成器启动序列”,而不是“用序列生成器开始音序”。序列生成器不执行序列,虚拟序列(virtual sequences)通常没有序列生成器。
    • 好的,这个“ 生成器”是挺烦的。你也看到这一切了。

 

想查看更多SystemVerilog和UVM提示吗?报名参加我即将举行的网络研讨会”UVM编码指南:你可能不知道的提示和技巧。”

享受你的验证旅程!

 

 

原文链接:https://blogs.mentor.com/verificationhorizons/blog/author/cspear/

点击【阅读原文】可直达课程页面,马上试听

 

往期精彩:

V2Pro春季班普遍学撑了,秋季班7月报名你还敢来么

30w+还送股送房?60+IC企业2019薪资全面攀升!

UVM RAL模型:用法和应用

我们准备做第二期线下培训,依旧认真且严肃

如果你突然被裁员了,你的Plan B是什么?

[彩虹糖带你入门UVM]

理解UVM-1.2到IEEE1800.2的变化,掌握这3点就够

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注