在我的公司,我们正在转向一个新的流程,在这个过程中,我们意外地遇到了VCS设置,即使在恢复到相同的VCS版本后,我们也无法修复它。以下是问题的psudocode代码。
EdaPlayground也复制到这里
现在我打算了解下面两行的执行顺序。根据这个顺序,程序块中显示的"socket_id"可能是25/-25。
- int socket_id=-25
- socket_id=25
Query1:如何配置VCS,使socket_id设置为25
我们使用的是VCS N-2017.12-SP2-6,它在两个不同的DB中给出了不同的结果。在一个数据库(旧数据库(中,我们将socket_id设置为"25",而在另一个数据库中(新数据库(,我们将其设置为"-25"。我在solvenet上提出了这个问题,在那里我被告知"-25"是正确的值。由于这是一个遗留代码,所以如果socket_id设置为25,它就会正常工作。但我不确定如何让VCS做到这一点。
Query2:为什么不同模拟器的代码输出不一致
此外,当我在EdaPlayground上尝试此代码时,我惊讶地发现,在不同的模拟器中,此代码的执行并不一致。看起来Mentor/Aldec做得不对,而VCS/Cadence的输出似乎是正确的。为了方便起见,我复制了下面的输出。
伪代码:
class Base;
function new();
$display("Base:new");
set_derived();
endfunction
virtual function void set_derived ();
$display("Base: set_drived");
endfunction
endclass
class Child extends Base;
int socket_id = -25;
int socket_id1;
function new();
super.new();
$display("Child:new");
$display("Child: new: socket_id = %d", socket_id);
endfunction
virtual function void set_derived ();
$display("Child: set_drived");
$display("Child: set_derived: socket_id = %d", socket_id);
socket_id = 25;
socket_id1 = 25;
$display("Child: set_derived: socket_id = %d", socket_id);
//super.set_drived();
endfunction
endclass
program test;
Child c;
initial begin
c = new();
$display("Program: socket_id = %d socket_id1 = %d", c.socket_id, c.socket_id1);
end
endprogram
来自不同模拟器的结果。
- Questa 2021.3
# Base:new
# Base: set_drived
# Child:new
# Child: new: socket_id = -25
# Program: socket_id = -25 socket_id1 = 0
- VCS 2020.03
Base:new
Child: set_drived
Child: set_derived: socket_id = 0
Child: set_derived: socket_id = 25
Child:new
Child: new: socket_id = -25
Program: socket_id = -25 socket_id1 = 25
- Cadence 20.09
Base:new
Child: set_drived
Child: set_derived: socket_id = 0
Child: set_derived: socket_id = 25
Child:new
Child: new: socket_id = -25
Program: socket_id = -25 socket_id1 = 25
- Aldec
# KERNEL: Base:new
# KERNEL: Base: set_drived
# KERNEL: Child:new
# KERNEL: Child: new: socket_id = -25
# KERNEL: Program: socket_id = -25 socket_id1 = 0
看起来questa和aldec不从构造函数调用虚拟方法,遵循c++范式,然而,SV标准至少有这个quot(8.17(:
注意2——当从构造函数new((调用虚拟方法时,构造函数会调用中所述的方法8.20。但是,用户必须知道8.7中描述的类属性初始化顺序,因为属性方法引用可能尚未初始化,这取决于该方法在构造函数链中的调用位置从…起
因此,questa和aldec不遵循标准。
8.7描述了初始化顺序:
如果类没有提供显式用户定义的新方法,则应提供隐式新方法自动地派生类的新方法应首先将其基类构造函数[super.new((调用为8.15]中描述的。在基类构造函数调用(如果有的话(完成后类应初始化为其显式默认值,如果未提供默认值,则初始化为其未初始化值。之后属性初始化后,应评估用户定义构造函数中的剩余代码。默认值构造函数在属性初始化后没有其他作用。财产在其之前的价值初始化应未定义。
因此,顺序如下:
- 子项::new->super.new((
- 基本::new->子级::set_derived,->socket_id=25
- 子项::socket_id->-25(类属性初始化(
- 孩子:新的,剩下的代码
因此,看起来vcs和cadence的行为符合标准,而questa和aldec则不然。
BTW,在面向对象的语言中,从构造函数调用虚拟函数被认为是危险的,因为初始化顺序混乱。
这是2012版IEEE 1800-2017 SystemVerilog LRM中的最新澄清。Questa有一个开关来获得新的行为(-svext=+vmctor
(,但在构造函数中使用虚拟方法在大多数情况下都被认为是一种糟糕的编程实践。
您应该注意到,当大多数人期望socket_id
用-25初始化时,它仍然具有值0。这是因为变量初始化是作为类构造函数执行的一部分发生的,但在基构造函数调用虚拟方法时,扩展类构造函数尚未执行。