Save time on re-compilation of the TB with an external configuration file

Save time on re-compilation of the TB with an external configuration file

The testbench I was working on took quite some time to compile, elaborate and output an executable file. The design is an interconnect that has mappings from different masters to various slaves and has service registers within it accessible by specific masters. There were a block of registers for each master that would control how the transactions from the master would behave, and this required writing a lot of sequences to configure and simulate different configurations for each register. I wanted a better way to run and test a particular register set configuration without re-compiling the entire testbench and design.

I created the register model using Synopsys's ralgen tool, and wrote a lot of sequences for exercising each register with random configuration. Since I was creating my own extension of the testbench (thanks to OOP) from an existing version with a separate environment and scoreboard, I had the chance to make a more flexible setup. The register model had a bunch of register blocks for each master.

  class master0_reg_block extends uvm_reg_block;
    rand   reg_main_ctl   m_reg_main_ctl;
    rand    reg_stat_ctl   m_reg_stat_ctl0;
    rand    reg_stat_ctl   m_reg_stat_ctl1;

And my sequences had the handle to the register model with which I can write, read and play with.

  class my_sequence extends uvm_sequence;
    reg_model   m_reg_model;
    task pre_body ();
      // Get reg_model from the configuration database
      uvm_config_db #(reg_model) :: get (null, "uvm_test_top", "reg_model", m_reg_model);
      // Do a write
      m_reg_model.m_master0_reg_block.m_reg_main_ctl.write (status, 32'hface_face);

If I wanted to try out different values of m_reg_main_ctl, which you might do during the testbench bring-up phase, it calls for a recompilation of the testbench. This is really a waste of time. What you need is more of a plug-in style of configuration that should save you time and effort.

The first thought is to use $value$plusargs, and this wears out when you have to configure multiple registers

  // Take the value provided if any, otherwise randomize
  if (!$value$plusargs ("main_ctl=%0h", main_ctl)) 

A better alternative would be to provide a register configuration text file with values to the different registers.

  main_ctl dead_beef
  stat_ctl0 1234_5678

Then parse the text file to obtain the values.

  function parse_reg_cfg ();
    integer fd = $fopen ("regCfgFile", "r");
    while (! $feof(fd)) begin
      $fscanf (fd, "%s %0d", name, value);
  task body ();
    string regCfgFile;
    if ($value$plusargs ("regCfgFile=%s", regCfgFile)) begin
      parse_reg_cfg ();
      m_reg_model.m_master0_reg_block.m_reg_main_ctl.write (status, main_ctl);
    end else begin

Once the testbench is compiled, you don't have to re-compile it when you edit the external register configuration text file. It's fine to leave the code in there once the testbench is more stable and is an actual advantage because it will allow you to feed configuration externally.

I always like to improve upon my methods, and feel free to comment on your own versions !

how to create a singleton object
What is SystemVerilog ?


No comments made yet. Be the first to submit a comment
Already Registered? Login Here
Saturday, 24 March 2018