《The UVM Primer》——chapter5: Classes and Extension

结构体开始

考虑两个用来保存几何形状数据的结构体:长方形正方形

我们使用以下信息来存储和打印有关这些形状的信息:

rectangle_structsquare_struct 来定义变量,然后用这些变量来保存大小并计算面积。

typedefstruct {
   int         length;
   int         width;
} rectangle_struct;

typedefstruct {
   int         side;
} square_struct;
  
  module top_struct ;
    rectangle_struct rectangle_s;
    square_struct       square_s;
    initialbegin
       rectangle_s.length = 50;
       rectangle_s.width  = 20;
       $display("rectangle area: %0d", rectangle_s.length * rectangle_s.width);
       square_s.side = 50;
       $display("square area: %0d", square_s.side ** 2);
    end
  endmodule
  • 尽管我们知道长方形跟正方形是有关系的,不过程序里没有体现。

  • 定义变量的时候,仿真器就为其分配内存了。

  • 如果不知道公式的话,就算不了长方形跟正方形的面积了。等到了我们需要extend到梯形和菱形时,这个问题可能就麻烦了。

定义一个类

下面是一个长方形类的定义,用这个例子来说明 OOP 的一些术语。

class rectangle;
    int length;
    int width;
    
    functionnew(int l, int w);
      length = l;
      width  = w;
    endfunction
    
    functionint area;
      return length * width;
    endfunction
  endclass : rectangle

  
  module top_rectangle ;

    rectangle rectangle_h;

    initialbegin

      rectangle_h = new(.l(50),.w(20));

      $display("rectangle area: %0d", rectangle_h.area());

    end
endmodule : top_rectangle

用 class 关键字来定义类,不像之前的结构体,这里不再用大括号来包住这个类,而是用 endclass 关键字。

我们定义了length和width类变量,在OOP中这个叫做类的数据成员。大多数类都是有数据成员的。定义了一个用数据成员返回长方形面积的area( )函数,任何可以访问长方形类的用户都可以使用这个函数。我们把定义在类中的函数或者任务叫做方法。这一步可以描述为,"长方形类有两个数据成员 length 和 width, 还有一个方法 area"。

在分配内存方面,仿真器对结构体和类是区别对待的。结构体一声明就分配内存了,而类不是这样。用户必须调用类的 new( )构造函数来为对象显式分配内存。

在这个例子中, 我们定义了 new( ),需要用户提供长度和宽度。创建长方形的时候,如果不提供这些信息是不可行的,这也会让我们在编译时而不是运行时就报错。

在module里使用类是这样的:

module top_class;
	rectangle rectangle_h;
	initialbegin
		rectangle_h = new(.l(50), .w(20));
		$display("rectangle area: %0d", rectangle_h.area());
	end
endmodule

首先声明了一个 rectangle 类型的变量。把它命名为 rectangle_h, 因为它是个指向长方形类的句柄。句柄类似于内存指针,不同的是句柄不能拿来运算。这句定义为句柄分配了足够的内存了,不过还没为对象分配内存。使用 new( )函数创建一个长度50、宽度20的长方形,是在这里为对象分配的内存。(函数调用中的点号表达式是 Verilog 中的用法,根据参数的顺序在构造函数中进行赋值)这个叫做例化对象。可以描述这一步为,"例化 rectangle 类的一个对象,并用 rectangle_h 变量来保存"。

可创建的对象的数量,仅仅受制于我们使用的计算机内存大小。我们可以将这些长方形的句柄复制到其他变量上,可以把句柄保存在数组中,也可以传入验证平台来控制仿真。我们操作对象的时候,SystemVerilog 会处理所有的内存分配。调用 new( )的时候为我们的新对象分配内存,我们不再引用的时候回收内存。比如,我们创建了第二个长方形对象并保存在rectangle_h变量中,仿真器会回收第一个长方形对象的内存。有了保存在 rectangle_h 变量中的 rectangle对象之后,我们可以用 area( )来计算长方形的面积了。

继承类

在之前的结构体例子中,定义了一个长方形结构体和一个正方形结构体,这两个结构体基本没啥重用的可能性。如果写了一个用 rectangle_struct 做参数的函数 area( ),就不能用 square_struct 做参数了。如果这个函数用长度和宽度做参数,就又破坏了封装的目的。

OOP 解决了这个重用问题,我们可以基于其他类来创建 SystemVerilog 类。

在这个例子中,我们注意到正方形就是边长相等的长方形。这意味着我们可以重用长度、宽度和面积。 这里用 extends 关键字来获得继承:

class rectangle;
    int length;
    int width;
    
    functionnew(int l, int w);
      length = l;
      width  = w;
    endfunction
    
    functionint area();
      return length * width;
    endfunction
  endclass
  
  class square extends rectangle;
  
    functionnew(int side);
      super.new(.l(side), .w(side));
    endfunction
  
  endclass
  
  module top_class ;
    rectangle rectangle_h;
    square    square_h;
    
    initialbegin
    
      rectangle_h = new(.l(50),.w(20));
      $display("rectangle area: %0d", rectangle_h.area());
      
      square_h = new(.side(50));
      $display("square area: %0d", square_h.area());
      
    end
  endmodule

上面的代码中首先定义了rectangle类,extends关键字向编译器指示了square类默认使用所有 rectangle 类中定义的数据成员和方法。不同之处是在这个类中重写的数据成员和方法。这个例子中重载了new( )构造函数,square类的 new( )只有一个参数:边长。函数使用这个边长,然后使用了OOP中的独门绝技super.new( )。super关键字指示编译器来显式的引用父类中定义的数据成员和方法。square中new( )函数中 super 关键字指示编译器调用rectangle中的构造函数并使用side传入长度和宽度参数。

我们在不用拷贝rectangle类代码的情况下实现了完全的重用,利用了 square 是 rectangle 的一个特例的性质,然后让编译器来做这个工作。在其他类之上定义类并且建立功能拓扑的能力是 OOP 和传统编程范式的关键的区别,当你把这个能力用在创建多个对象时,你就拥有了一个可以创建软件和验证平台的强力工具平台。

想一想:上面这个例子中有一个 rectangle_h 变量和一个 square 对象。既然 square 是一个 rectangle,那我们可以用 rectangle_h 变量来存 square 吗?

这是可以的,这个叫做多态,将在下一章探讨。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值