{"id":16625,"date":"2021-10-27T00:40:00","date_gmt":"2021-10-27T04:40:00","guid":{"rendered":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/?p=16625"},"modified":"2026-03-27T08:47:50","modified_gmt":"2026-03-27T12:47:50","slug":"the-configuration-database-in-pyuvm","status":"publish","type":"post","link":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/2021\/10\/27\/the-configuration-database-in-pyuvm\/","title":{"rendered":"The configuration database in pyuvm"},"content":{"rendered":"\n\n\n<h1>The configuration database<\/h1>\n\n<p>In the previous post in the <a href=\"https:\/\/verificationacademy.com\/news\/the-python-for-verification-series\" target=\"_blank\" rel=\"noopener\">Python for Verification Series<\/a> we discussed how <strong>pyuvm<\/strong> implemented <a href=\"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/2021\/10\/01\/tlm-1-0-in-pyuvm\/\">TLM 1.0<\/a>. Now we turn our attention to some of the UVM\u2019s utilities and how we use them in Python. The first of these is the UVM configuration database.<\/p>\n\n<p>The configuration database as defined in the IEEE 1800.2 specification is very complicated. It includes a <code>uvm_resource_db<\/code> that handles storing data of different types with the <code>uvm_config_db<\/code> as a user interface for the <code>uvm_resource_db<\/code>.<\/p>\n\n<p>None of this was necessary for <code>pyuvm<\/code> since there are no types, so I refactored it and named it <code>ConfigDB<\/code> to avoid confusion with the SystemVerilog version. In this post we\u2019ll examine <code>ConfigDB()<\/code>.<\/p>\n\n<h2>What is the <code>ConfigDB()<\/code>?<\/h2>\n\n<p>The <code>ConfigDB<\/code> class is a singleton database that stores objects using keys. It is much like a Python dictionary except that the keys must be strings and we can control the visibility to the data using the UVM hierarchy.<\/p>\n\n<p>In this blog post I refer to the configuration database as the <code>ConfigDB()<\/code> as a reminder that we always call the <code>ConfigDB<\/code> class (using parentheses) to get the handle to the singleton.<\/p>\n\n<h2>Basic Setting and Getting<\/h2>\n\n<p>We\u2019ve already seen the most basic use of <code>ConfigDB()<\/code> in the <a href=\"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/2021\/09\/09\/python-and-the-uvm\/\">Python and the UVM<\/a> introductory blog post. We call our UVM testbench using a <strong>cocotb<\/strong> test. The test takes the <code>dut<\/code> argument and uses it to create an instance of <code>TinyAluBFM<\/code> then it stores the <code>bfm<\/code> in the <code>ConfigDB()<\/code> using the label <code>BFM<\/code>:<\/p>\n\n<pre><code>@cocotb.test()\nasync def test_alu(dut):\n    bfm = TinyAluBfm(dut)\n    ConfigDB().set(None, \"*\", \"BFM\", bfm)\n    await bfm.startup_bfms()\n    await uvm_root().run_test(\"AluTest\")<\/code><\/pre>\n\n<p>We see here that the <strong>pyuvm<\/strong> version of <code>ConfigDB()<\/code> is much simpler than the SystemVerilog one since we don\u2019t have to deal with parameterized types or a litany of static function calls. Instead we simply say <code>ConfigDB().set()<\/code>.<\/p>\n\n<p>We get the BFM from <code>ConfigDB()<\/code> in, for example, the <code>Driver<\/code> class. Here we get the <code>bfm<\/code> in the <code>connect_phase()<\/code>:<\/p>\n\n<pre><code>class Driver(uvm_driver):\n    def connect_phase(self):\n        self.bfm = ConfigDB().get(self, \"\", \"BFM\")\n\n    async def run_phase(self):\n        while True:\n            command = await self.seq_item_port.get_next_item()\n            await self.bfm.send_op(command.A, command.B, command.op)\n            self.logger.debug(f\"Sent command: {command}\")\n            self.seq_item_port.item_done()<\/code><\/pre>\n\n<p>This approach of using <code>None, '*<\/code> when setting the data and <code>self, \"\"<\/code> when getting the data is how we store global data. We\u2019ll now look at storing data at specific places in the hierarchy.<\/p>\n\n<h2>UVM hierarchical control<\/h2>\n\n<p>We use the <code>ConfigDB()<\/code> instead of a dictionary so that we can instantiate the same component class in different parts of the testbench, and have them retrieve different data using the same label. That way we don\u2019t have to modify the code or somehow pass the label to the object. <\/p>\n\n<p>The <code>ConfigD()<\/code> uses a combination of a handle to a UVM component and a string to create a UVM path both to store data in the <code>ConfigDB()<\/code> and to retrieve it. The first two arguments to both the <code>set()<\/code> and <code>get()<\/code> functions are the component and the string path. Here is how this applies to the above call to <code>set()<\/code>:<\/p>\n\n<pre><code>    ConfigDB().set(None, \"*\", \"BFM\", bfm)<\/code><\/pre>\n\n<p>The UVM component in this case is set to <code>None<\/code>. This gives us an empty base path of <code>\"\"<\/code>. The path string in this case is <code>\"*\"<\/code> and so this means we store <code>\"BFM\"<\/code> with the path <code>\"*\"<\/code>, which matches everything. <\/p>\n\n<p>The call to <code>get()<\/code> looks like this:<\/p>\n\n<pre><code>self.bfm = ConfigDB().get(self, \"\", \"BFM\")<\/code><\/pre>\n\n<p>In this case the <code>self<\/code> variable contains an instance of the <code>Driver<\/code>. The <code>get()<\/code> function uses <code>get_full_name()<\/code> on the <code>self<\/code> variable to get the path to driver: <code>\"uvm_test_top.env.driver\"<\/code>. Then it concatenates the string in the second argument (<code>\"\"<\/code>) to add nothing to the base path.<\/p>\n\n<p>Finally the <code>ConfigDB()<\/code> uses the Python <code>fnmatch<\/code> function to see if the path in <code>set()<\/code> matches the path in <code>get()<\/code>. <code>u\"*\"<\/code> matches <code>uvm_test_top.env.driver<\/code> and so we read the data.<\/p>\n\n<p>We use this system to store different data, say different BFMs, in different parts of the testbench hierarchy. Here is a simple example:<\/p>\n\n<p>We create a simple component that pulls a message out of the <code>ConfigDB()<\/code> and logs it:<\/p>\n\n<pre><code>class MsgLogger(uvm_component):\n\n    def build_phase(self):\n        self.msg = ConfigDB().get(self, \"\", \"MSG\")\n\n    async def run_phase(self):\n        self.raise_objection()\n        self.logger.info(self.msg)\n        self.drop_objection()<\/code><\/pre>\n\n<p>Now we\u2019ll instantiate this component in two places.<\/p>\n\n<pre><code>class MsgEnv(uvm_env):\n    def build_phase(self):\n        self.loga = MsgLogger(\"loga\", self)\n        self.logb = MsgLogger(\"logb\", self)<\/code><\/pre>\n\n<p>Finally we\u2019ll use the <code>ConfigDB()<\/code> to pass two different messages:<\/p>\n\n<pre><code>class LogTest(uvm_test):\n\n    def build_phase(self):\n        self.env = MsgEnv(\"env\", self)\n        ConfigDB().set(self, \"env.loga\", \"MSG\", \"AAAAA\")\n        ConfigDB().set(self, \"env.logb\", \"MSG\", \"BBBBB\")<\/code><\/pre>\n\n<p>We\u2019ve used <code>self<\/code> as the context object. The <code>LogTest<\/code> object will be instantiated with the name <code>uvm_test_top<\/code> so that will be the base path for storing the data. If we ever use <code>LogTest<\/code> as part of a larger testbench the <code>ConfigDB()<\/code> will work same way because <code>self<\/code> would generate the full path to the place we were instantiated.<\/p>\n\n<p>In this case, the <code>self<\/code> path is <code>uvm_test_top<\/code> and we use the path string in the second argument to create <code>uvm_test_top.env.loga<\/code> and <code>uvm_test_top.env.logb<\/code>. Now each instance of <code>MsgLogger<\/code> will get a different message:<\/p>\n\n<pre><code>INFO: testbench.py(12)[uvm_test_top.env.loga]: AAAAA\nINFO: testbench.py(12)[uvm_test_top.env.logb]: BBBBB<\/code><\/pre>\n\n<h3>Path overrides<\/h3>\n\n<p>One can imagine a situation where a UVM components sets a value in the <code>ConfigDB()<\/code> at a place in its hierarchy, but then gets instantiated inside another component that sets a different value to the same path.<\/p>\n\n<p>The IEEE 1800.2 UVM specification describes what to do in this situation: <em>If the components set the configuration values in the <code>build_phase()<\/code> function then the higher level component\u2019s <code>set()<\/code> overrides the lower level component\u2019s <code>set()<\/code>.<\/em><\/p>\n\n<p>For example, let\u2019s have the environment set a value for <code>loga<\/code>:<\/p>\n\n<pre><code>class MsgEnv(uvm_env):\n    def build_phase(self):\n        self.loga = MsgLogger(\"loga\", self)\n        self.logb = MsgLogger(\"logb\", self)\n        ConfigDB().set(self, \"loga\", \"MSG\", \"AAAENVAAA\")<\/code><\/pre>\n\n<p>This means that <code>\"AAAENVAAA\"<\/code> is set for <code>uvm_test_top.env.loga<\/code>. Remember, however that <code>LogTest<\/code> has set a different value (<code>\"AAAAA\"<\/code>) to the same path.<\/p>\n\n<pre><code>class LogTest(uvm_test):\n\n    def build_phase(self):\n        self.env = MsgEnv(\"env\", self)\n        ConfigDB().set(self, \"env.loga\", \"MSG\", \"AAAAA\")\n        ConfigDB().set(self, \"env.logb\", \"MSG\", \"BBBBB\")<\/code><\/pre>\n\n<p>The result? The test wins since it is the higher level component.<\/p>\n\n<pre><code>INFO: testbench.py(12)[uvm_test_top.env.loga]: AAAAA\nINFO: testbench.py(12)[uvm_test_top.env.logb]: BBBBB<\/code><\/pre>\n\n<h2>Debugging the <code>ConfigDB()<\/code><\/h2>\n\n<p>The <code>ConfigDB()<\/code> has several debugging mechanism. One comes from the UVM specification. You can set the <code>ConfigDB().is_tracing<\/code> class variable to <code>True<\/code>:<\/p>\n\n<pre><code>@cocotb.test()\nasync def log_msgs(dut):\n    \"\"\"Exercise hierarchy storage\"\"\"\n    ConfigDB().is_tracing = True\n    await uvm_root().run_test(\"LogTest\")\n    print(ConfigDB())\n    assert True<\/code><\/pre>\n\n<p>Now the <code>ConfigDB()<\/code> will print out all the <code>set()<\/code> and <code>get()<\/code> operations. Notice that we see both <code>set()<\/code> calls using the same path.<\/p>\n\n<pre><code># CFGDB\/SET Context: uvm_test_top  --  uvm_test_top.env.loga MSG=AAAAA\n# CFGDB\/SET Context: uvm_test_top  --  uvm_test_top.env.logb MSG=BBBBB\n# CFGDB\/SET Context: uvm_test_top.env  --  uvm_test_top.env.loga MSG=AAAENVAAA\n# CFGDB\/GET Context: uvm_test_top.env.loga  --  uvm_test_top.env.loga MSG=AAAAA\n# CFGDB\/GET Context: uvm_test_top.env.logb  --  uvm_test_top.env.logb MSG=BBBBB<\/code><\/pre>\n\n<p>And as final bonus, you can print <code>ConfigDB()<\/code> as we see in the last line of the test. This gives the database. You can see how the <code>ConfigDB()<\/code> has prioritized the two conflicting values:<\/p>\n\n<pre><code># PATH                : FIELD     : DATA                          \n# uvm_test_top.env.loga: MSG       : {999: 'AAAAA', 998: 'AAAENVAAA'}\n# uvm_test_top.env.logb: MSG       : {999: 'BBBBB'}<\/code><\/pre>\n\n<h2>Summary<\/h2>\n\n<p>In this blog post we examined the <code>ConfigDB()<\/code>, a refactored Python version of the <code>uvm_config_db<\/code> and the <code>uvm_resource_db<\/code>. We saw how to store values globally and at a specific location in the UVM hierarchy. <\/p>\n\n<p>We also saw how the UVM specification defined the response to conflicting values for a given key with the same path, and the prioritization behavior that happens only in the <code>build_phase<\/code>.<\/p>\n\n<p>Finally we saw how to debug the <code>ConfigDB()<\/code> using the <code>is_tracing<\/code> variable and the <code>print()<\/code> function<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The configuration database In the previous post in the Python for Verification Series we discussed how pyuvm implemented TLM 1.0&#8230;.<\/p>\n","protected":false},"author":74314,"featured_media":16629,"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":[984,1,981,10],"tags":[606,902,971,787,790,791],"industry":[],"product":[],"coauthors":[947],"class_list":["post-16625","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cocotb","category-news","category-pyuvm","category-tips-tricks","tag-multilanguage-uvm","tag-python","tag-pyuvm","tag-uvm","tag-uvm-config","tag-uvm-configuration-database"],"featured_image_url":"https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/54\/2021\/10\/pyuvmconfigdb.png","_links":{"self":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts\/16625","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\/74314"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/comments?post=16625"}],"version-history":[{"count":5,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts\/16625\/revisions"}],"predecessor-version":[{"id":16650,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts\/16625\/revisions\/16650"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/media\/16629"}],"wp:attachment":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/media?parent=16625"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/categories?post=16625"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/tags?post=16625"},{"taxonomy":"industry","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/industry?post=16625"},{"taxonomy":"product","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/product?post=16625"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/coauthors?post=16625"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}