Jump to content

Should UVM Analysis Interface write const objects


Recommended Posts

The current write method sends a handle to a non-const object. This allows any subscriber to a uvm_analysis_port to modify the object in the monitor. This is BAD.

 

To prevent this it would be ideal to make the write method take a const object. However, in SV the const keyword only affects the handle, not the object pointed to by the handle. In C++ you can make what the pointer is pointing to const. I don't see a way to do that in SV. Making the handle const is not useful in this case.

 

With this language shortcoming, the monitor could copy its transaction before doing the write, thus ensuring that its copy can't be modiified. This would work, but a performance penalty would be paid for every write to an analysis port whether it was needed or not. If the object was declared const then it would force the subscriber to make a non-const copy only when needed.

 

Am I understanding the limitation of SV correctly? If so, is there a request to fix it? If that happened, shouldn't the TLM interfaces be modified to pass const objects?

 

Link to comment
Share on other sites

AFAIK, subroutine calls in SystemVerilog use pass by reference semantics. This means that what you say (that the const keyword only affects the handle) doesn't make sense, seeing as how input references can't anyway be modified. The LRM specifically states in Section 13.5.2 Pass by reference that using the const ref qualifier for an input argument means that the value pointed by the handle cannot be modified and any attempt to do so results in a compile error. Here's an example on EDAPlayground that shows this in action.

 

The UVM analysis port doesn't indeed use constant input arguments. At this point I'm afraid to speculate anymore why, but I'd expect that it's because (as Dave Rich already pointed out for other SV constructs) this language feature wasn't supported by all major simulators at the time when the UVM BCL was being implemented.

Link to comment
Share on other sites

The analysis ports came from SystemC TLM2, where the write method is defined with const refs in C++, but since some of the fields in the SystemC payloads are pointers, and hence could be altered, the SystemC standard specifically made rules to say that these arguments should not be modified:

 

"j) The write method shall not modify the transaction object passed as a const reference argument, nor
shall it modify any data associated with the transaction object (such as the data and byte enable
arrays of the generic payload).

 

k) If the implementation of the write method in a subscriber is unable to process the transaction before
returning control to the caller, the subscriber shall be responsible for taking a deep copy of the
transaction object and for managing any memory associated with that copy thereafter."

 

So in the original SystemC TLM implementation it was certainly intended that the arguments were const ref, and that the arguments should not be altered.

 

regards

Alan

Link to comment
Share on other sites

@Alan I guess you mean SystemC TLM, not TLM2. In any case, that's pretty good feedback. I've updated the example on EDAPlayground and there it seems that modifying any field of an inner object isn't allowed if the handle to the parent object is defined as const.

 

Not quite. The example in 13.5.2 applies only to arrays. Arrays in SV are value objects (do not exhibit pointer-semantics as in C/C++). So when we make an array const, the const applies to array members as well.

 

13.5.2 has to be read along with section 6.20.6 which states:

 

An instance of a class (an object handle) can also be declared with the const keyword.
const class_name object = new(5,3);
In other words, the object acts like a variable that cannot be written. The arguments to the new method shall
be constant expressions (see 11.2.1). The members of the object can be written (except for those members
that are declared const).

 

 

So, clearly const does not propagate to the object members. The example you provided on EDA Playground seems like a tool bug. The example compiles and runs fine when we select modelsim 10.1d on EDA Playground.

 

BTW, what the OP posted is a long standing issue http://verificationguild.com/modules.php?name=Forums&file=viewtopic&t=1490

Link to comment
Share on other sites

  • 3 weeks later...

@kirloy if you notice, what @tudor demonstrated is hierarchical immutability (const is getting propagated to sub-objects as well) which is not even possible with C++ as pointed out by @apfitch-- though some modern programming languages are known to provide that feature.

 

Though it would indeed be great to have such a feature in systemverilog, unfortunately @tudor's example only demonstrates a tool bug -- considering that this slightly modified code example that I created is breaking the illusionary immutability. Take a look:

 

 http://www.edaplayground.com/x/8YD

Link to comment
Share on other sites

@puneet You're half right with your hierarchical immutability statement. The C++ compiler doesn't follow through pointers and references, but if I have a data member defined that is an object, then I can't change any of its fields. Here's a snippet of C++ code I've tried this out with:

#include <iostream>

struct some_inner_struct {
  int some_inner_field;

  some_inner_struct() {
    some_inner_field = 0;
  }
  
  void print() {
    std::cout<<"some_inner_field = "<<some_inner_field<<std::endl;
  }
};

struct some_struct {
  int some_field;
  some_inner_struct some_obj;
  some_inner_struct& some_ref;
  some_inner_struct* some_pointer;

  some_struct() : some_ref(some_obj) {
    some_field = 0;
  }
  
  void print() {
    std::cout<<"some_field = "<<some_field<<std::endl;
    std::cout<<"some_obj: ";
    some_obj.print();

    std::cout<<"some_ref: ";
    some_ref.print();
    
    if (some_pointer != NULL) {
      std::cout<<"some_pointer: ";
      some_pointer->print();
    }
  }
};

void some_function(const some_struct& arg) {
  // this isn't allowed because it's trying to modify a data member of
  // the argument
  //arg.some_field = 5;

  // this also isn't allowed, because the compiler knows that 'some_obj'
  // is a data member of 'arg'
  //arg.some_obj.some_inner_field = 5;

  // the following are allowed because the compiler doesn't follow refs and
  // pointers, i.e. it only treats the contents as data members
  arg.some_ref.some_inner_field = 5;
  arg.some_pointer->some_inner_field = 5;
}

int main() {
  some_struct obj;
  some_inner_struct inner_obj;
  obj.some_pointer = &inner_obj;
  
  std::cout<<"before"<<std::endl;
  obj.print();
    
  some_function(obj);
  
  std::cout<<"after"<<std::endl;
  obj.print();
  
  return 0;
}

Notice that if I try to change any of the fields of some_obj in some_function(...), I'd get a compile failure.

Link to comment
Share on other sites

We're also comparing apples with oranges here in this thread. A reference in C++ is a different concept from a reference (to an object) in SystemVerilog (which is more like a reference in Java). SystemVerilog decided to take elements from both languages (the pass by whatever semantics of Java when passing objects together with allowing pass by value and by reference as in C++) and mix them into a nice soup of confusion.

 

Java doesn't allow immutable arguments (because there "the general ideology is that access policy to the state of an object is controlled by the class" - according to this site). C++ does provide this, because it doesn't discourage designs with a lot of public fields (according to the same site). SystemVerilog also encourages such designs (think of all the nice fields that are declared as rand), but doesn't allow any concept of immutable arguments.

 

Maybe in a future version of the standard we could get something like some_function(const some_class arg) which would be equivalent to a constant reference in C++. Or maybe if the developers do implement it, we'll see the less useful behavior of Java's final arguments instead.

Link to comment
Share on other sites

@puneet You're half right with your hierarchical immutability statement. The C++ compiler doesn't follow through pointers and references, but if I have a data member defined that is an object, then I can't change any of its fields.

 

@tudor C++ const does not propagate across pointer boundaries. I was just referring to that simple and well known fact.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...