{"id":1927,"date":"2011-02-13T17:05:31","date_gmt":"2011-02-14T00:05:31","guid":{"rendered":"https:\/\/blogs.mentor.com\/verificationhorizons\/?p=1927"},"modified":"2026-03-27T08:52:13","modified_gmt":"2026-03-27T12:52:13","slug":"parameterized-classes-static-members-and-the-factory-macros","status":"publish","type":"post","link":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/2011\/02\/13\/parameterized-classes-static-members-and-the-factory-macros\/","title":{"rendered":"Parameterized Classes, Static Members and the Factory Macros"},"content":{"rendered":"<p>Somebody asked me a simple question: Why do need two different macros (`ovm_object_utils and `ovm_object_param_utils) to register classes with the factory, and why can\u2019t it tell me when I\u2019ve used the wrong one? The answer turns out to be quite long, and demonstrates the dangers of using certain macros without first understanding the code behind them. So I\u2019m posting the response here. Adam Erickson will be presenting a paper <i>Are Macros in OVM &amp; UVM Evil?<\/i> at the upcoming DVCon11 on Wednesday, March 2<sup>nd<\/sup> that goes into more details about the costs and benefits of the OVM macros.<\/p>\n<p>First I need to talk about parameterized classes in SystemVerilog and how they interact with static members of those kinds of classes.<\/p>\n<p>When you declare a parameterized class, it is more like a template, or <i>generic<\/i> class than a real class type. Only <i>specializations<\/i> of parameterized classes are real types. Suppose I have the two class definitions in the table below:<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"319\">Un-parameterized<\/td>\n<td valign=\"top\" width=\"319\">Parameterized<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"319\">\n<pre>class A;\n static string name = \u201cpacket\u201d;\nendclass<\/pre>\n<\/td>\n<td valign=\"top\" width=\"319\">\n<pre>class B #(int w=1);\n static string name = \u201cpacket\u201d;\nendclass<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The class A definition by itself creates the static variable A::name initialized with the string \u201cpacket\u201d without any other reference to class A. As soon as you add parameters to a class definition, like the parameter w in class B, the class becomes a <i>generic<\/i> class and the static variable name does not get created. As soon as there are specializations of the class B, each unique specialization causes the static variables inside the class to be instantiated.\u00a0 The following statements create two specializations of class B and two instances of the static variable.\u00a0 B#(2)::name and B#(3)::name are both set to \u201cpacket\u201d.<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"638\">\n<pre>typedef B#(2) B2;\nB#(3) B3_1h;\nB#(3) B3_2h;<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The two class variable declarations (B3_1h and B3_2h) represent only one unique specialization of B because its parameters have the same value in both declarations. The variable B#(1)::name does not exist unless there is some other reference to B or B#(1) somewhere else.<\/p>\n<p>What if you wanted the static string variable name to have a different value for each unique specialization of B? You could write something like<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"638\">\n<pre>class B #(int w=1);\ns tatic string name = $sformatf(\u201cpacket%0d\u201d,w);\nendclass<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Now assuming the previous typedef and variable declarations above, B#(2)::name would have the value \u201cpacket2\u201d and B#(3)::name would have the value \u201cpacket3\u201d. There would be no instance B#(1)::name and the string \u201cpacket1\u201d would never have been generated.<\/p>\n<p>Now let us go back to the `ovm_object_utils macro. Suppose we have the following class definition<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"663\">\n<pre>`include \"ovm_macros.svh\"\nimport ovm_pkg::*;\nclass packetA extends ovm_object;\n  `ovm_object_utils(packetA)\n  \u2026\nendclass<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Looking at just the factory registration statement this macro inserts for us (this little one line macro expands to over 100 lines of code just to support the field automation macros), we see a typedef for a specialization of the parameterized class ovm_object_registry called type_id.<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"638\">\n<pre>import ovm_pkg::*;\nclass packetA extends ovm_object;\n<b>\u00a0 typedef ovm_object_registry#(packetA,\"packetA\") type_id;\n<\/b>  static function type_id get_type();\n  return type_id::get();\nendfunction\n\u2026\nendclass<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The specialized class type_id gives us access to all the static declarations inside ovm_object_registry. The code inside that class does something similar to what class A did above, except that it builds a global list of all string names and their associated types that can be used by the factory. The OVM gives you the choice of using the string name \u201cpacket\u201d or the static function packetA::get_type() to set overrides, depending on which factory methods you use. The problem using the string names is that there is no type checking until run-time when the override statements are executed. We prefer you use type references to perform overrides<\/p>\n<pre>packetA::type_id::set_inst_override(extended_packetA::get_type(),\u201denv.my_agent.*\u201d);<\/pre>\n<p>Finally, let us take a look at a parameterized class, but assume we used the same `ovm_object_utils macro.<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"638\">\n<pre>import ovm_pkg::*;\nclass packetB #(int w=1) extends ovm_object;\n<b>\u00a0 typedef ovm_object_registry#(packetB#(w),\"packetB#(w)\") type_id;\n<\/b>  static function type_id get_type();\nreturn type_id::get();\nendfunction\n\u2026\nendclass<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>There are two problems here. The first is that this is now a generic class. The string \u201cpacketB#(w)\u201d will not put on the factory registration list unless there is a specialization of the class packetB somewhere. The second is that if there are more than one specializations of packetB, they all will be registered with the same string name, producing an error at run time.<\/p>\n<p>The `ovm_object_param_utils macro simply leaves the second parameter to ovm_object_registry as the null string and forces you to use type references for your overrides. These type references also create the specializations needed to create the static methods inside these classes.<\/p>\n<pre>packetB#(2)::type_id::set_inst_override(\nextended_packetB#(2)::get_type(),\u201denv.my_agent.*\u201d);<\/pre>\n<p>The references to packetB#(2) and extended_packetB#(2) are checked at compile time and cause the static methods within these references to be created.<\/p>\n<p>You can use $psprintf to register a string name as long as the string is unique for each specialization of the class. This can be difficult when the parameters are types.<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"638\">\n<pre>import ovm_pkg::*;\nclass packetB #(int w=1) extends ovm_object;\nparameter string name = $sformatf(\u201cpacketB%0d\u201d,w);\n<b>\u00a0 typedef ovm_object_registry#(packetB#(w),name) type_id;\n<\/b>\u2026<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>OK, I\u2019m done. If you still need more background information. I recommend another DVCon09 paper I wrote with Adam about <a href=\"https:\/\/resources.sw.siemens.com\/en-US\/white-paper-using-parameterized-classes-and-factories-the-yin-and-yang-of-object\" target=\"_blank\" rel=\"noopener\">Using Parameterized and Factories<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Somebody asked me a simple question: Why do need two different macros (`ovm_object_utils and `ovm_object_param_utils) to register classes with the&#8230;<\/p>\n","protected":false},"author":71589,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spanish_translation":"","french_translation":"","german_translation":"","italian_translation":"","polish_translation":"","japanese_translation":"","chinese_translation":"","footnotes":""},"categories":[1],"tags":[623,751,827],"industry":[],"product":[],"coauthors":[979],"class_list":["post-1927","post","type-post","status-publish","format-standard","hentry","category-news","tag-ovm","tag-systemverilog","tag-verification-methodology"],"_links":{"self":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts\/1927","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/users\/71589"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/comments?post=1927"}],"version-history":[{"count":5,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts\/1927\/revisions"}],"predecessor-version":[{"id":18819,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts\/1927\/revisions\/18819"}],"wp:attachment":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/media?parent=1927"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/categories?post=1927"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/tags?post=1927"},{"taxonomy":"industry","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/industry?post=1927"},{"taxonomy":"product","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/product?post=1927"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/coauthors?post=1927"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}