SystemVerilog中不同句柄之间的动态类型转换
多态(polymorphism)是面向对象编程的核心思想之一,指同一个行为具有多个不同表现形式或形态的能力,同一消息可以根据发送对象的不同而采用多种不同的行为方式,在SystemVerilog中指不同的子类在继承父类后分别都重写覆盖了父类的方法,即父类同一个方法,在继承的子类中表现出不同的形式,所以多态和继承是密不可分的,(关于多态可以参考《【107】多态和虚方法》)。
因为SystemVerilog中继承和多态特性的存在,父类中的变量和方法(未声明为local和protect时)对于子类是可见的,所以可以将子类句柄复制给父类句柄实现通过父类句柄访问子类对象,如下例所示。
示例中,子类句柄c_ahb指向创建对象之后,访问的属性(data和sub_data)和方法disp为自身的;当把子类句柄复制给父类句柄后,可以通过父类句柄访问父类句柄类型的所有属性和方法,但是不能访问子类句柄指向的子类对象中的属性和方法,虽然子类中也有属性data和str,但是因为父类句柄的类型为父类,所以其所能访问的属性仍为句柄类型中的属性。示例中的这种将子类句柄复制给父类句柄的过程是一种父类句柄向下类型转换。子类既然继承了父类,就拥有父类的一切属性,除此之外,子类还可以有自己独特的属性,这些是父类没有的,在进行向下类型转换时,相当于父类的句柄指向子类对象,这样的话句柄仍然能对子类对象与父类相同的属性进行访问。
那么,除了上例中将子类句柄复制给父类句柄外,可不可以将父类句柄复制给子类句柄呢?即可以不可以实现子类句柄的向上转换呢?
【示例】
示例中第37行,通过将父类句柄复制给子类句柄这种子类句柄的向上转换行为会在进行编译时报错,即在SV中不允许直接将父类句柄复制给子类句柄。其实在SystemVerilog中提供了一种系统任务和函数$cast,可以实现父类句柄指向对象和子类句柄指向对象的动态转换,从而实现父类句柄复制给子类句柄的操作,如下例。
【示例】
示例中,虽然代码在编译时通过了,但是在仿真$cast被执行到时因为类型不匹配导致转换错误。同时从这个示例也可以注意到$cast是在仿真执行的过程中执行的,不是在编译阶段完成的,所以使用$cast是一种动态的类型转换。虽然在SystemVerilog中可以使用$cast实现子类句柄的向上转换,但是这种转换并不是不分青红皂白的直接将父类句柄转换成子类句柄,$cast在转换时需要检查要转换的两个句柄指向对象的类型,如果转换源类型与目的类型所指向对象的类型一致或者是目的类型指向的对象是源类型的父类,那么可以使用$cast进行转换。示例中,因为f_ama指向的对象不是目的类型句柄指向的类型,而是目的类型的父类,所以这样的转换将会失败。正确的使用方式如下例所示。
【示例】
这里使用到的$cast在SystemVerilog中存在两种使用方式,一种是任务方式,一种是函数方式。示例中使用的是$cast的任务方式,如果类型不匹配的话仿真将会报错。如果使用$cast的函数方式,那么将不会报错,而是会返回一个int型的数据,非零表示转换成功,如下例所示。
【示例】
具体在实际使用过程中,$cast是作为任何还是作为函数使用,仿真时会根据使用的上下文决定具体调用的方式。
通过上面几个示例可以知道如下几点:
l 如果将子类句柄复制给父类句柄,可以实现父类句柄的向下转换,可以不使用$cast完成,属于一种静态的转换,如果类型不匹配会在编译时报错;
l 如果将父类句柄复制给子类句柄,可是实现子类句柄的向上转换,但是必须需要使用$cast实现,并且父类句柄指向的对象类型要与子类句柄相同,否则转换失败;
l 对于类中的属性,SV会根据句柄handle本身的类型进行判定,父类句柄指访问的仍然是父类句柄类型中的属性;
l $cast是作为任务还是作为函数使用,仿真时会根据使用的上下文决定具体调用的方式