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; ... endclass
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); endtask endclass
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)) main_ctl.randomize();
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); ... end endfunction 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 m_reg_model.m_master0_reg_block.m_reg_main_ctl.randomize(); end endbody
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 !