Facebook/LinkedIn login is now deprecated, please disconnect our access to your social profile.
Let us contribute to a cleaner Earth, Go Green Updated: May 31, 2020

By accepting you will be accessing a service provided by a third-party external to https://www.chipverify.com/

3 minutes reading time (668 words)

HDL routines for error injection


There are times when a signal within the design has to be probed or monitored for certain tasks in the testbench. Typically these are accessed via hierarchical references and tend to break when the same code is ported to different projects because of changes in design hierarchy. UVM has a set of DPI implementation tasks for backdoor accesses that has a similar effect and achieves much better code reusability.

The fact that hierarchical paths can now be represented as strings brings about all kinds of new possibilities. This means that they can be stored in associative arrays and queues, modified based on project dependencies, and a lot more. This is particularly useful for verification of design resilience by error injection where the design itself does not have registers to test its resiliency against soft errors. In this post, we'll look at a simple example of how these strings can be stored in queues and used at many places.

Consider a simple design block just for the sake of having hierarchical elements. Lets assume we have a common design block called A which is used across two different projects prj_B and prj_C.

module B ();
  reg [3:0] cfg;
  reg [5:0] mode;
module A ();
  reg enable;
  reg [15:0] addr;
  reg [3:0] data;
  B b();
module prj_B ();
  A a1();
module prj_C ();
  A a2();

Lets assume we have two separate testbench structures for each of these projects.

module tb_B;
  prj_B  pb;
module tb_C;
  prj_C  pc;

Traditionally, these signals have to be accessed via the whole hierarhical path like shown below. These paths are hardcoded and require a lot of effort fo maintainence across projects. If the same block of code was written in a task, the task cannot be reused in prj_C because instance name is a2 instead of a1.

module tb_B;
  prj_B pb;
  initial begin
    $deposit(pb.a1.b.cfg, 4'hd);
    #10 force pb.a1.b.cfg = 4'h7;
    #40 release pb.a1.b.cfg;

We have more flexibility in UVM to handle this since paths are stored as strings and there are methods that can do operations on these paths. For example, we can have these paths stored in an external file that can be parsed and extracted into a queue in our testbench. Let's assume we have a new file called hdl_paths with the following contents:


Now we can have a separate function to parse all these paths from the external file.

class base_test extends uvm_test;
  string q[$];
  virtual function get_hdl_paths();
    string path;
    int fd = $fopen ("hdl_paths", "r");
    if (fd) begin
      while ($fscanf (fd, "%s", path) == 1) begin
    end else 
      `uvm_fatal ("NOT_FOUND", $sformatf ("File not found"))
  virtual function void build_phase (uvm_phase phase);

Single bit errors can now be injected into these signal paths using another function.

  virtual function void single_bit_error();
    int idx, bitnum;
    string path;
    bit[63:0] org_val, err_val, new_val;
    std::randomize(idx) with { idx >= 0; idx < q.size(); };
    path = q[idx];
    std::randomize(bitnum) with { bitnum >= 0; bitnum < msb[path] - 1; };
    `uvm_info ("single_bit_error", $sformatf ("Inject single bit error into bit=%0d path=%s idx=%0d", 
                                                                   bitnum, q[idx], idx), UVM_MEDIUM)
    uvm_hdl_read(q[idx], org_val);
    err_val[bitnum] = ~err_val[bitnum];
    uvm_hdl_deposit(q[idx], err_val);
    uvm_hdl_read(q[idx], new_val);
    `uvm_info ("single_bit_error", $sformatf ("Org val=0x%0h Err val=0x%0h New val=0x%0h", 
                                                              org_val, err_val, new_val), UVM_MEDIUM)

In a similar way double bit errors and other mechanisms can be implemented well with UVM's HDL access routines.

Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO error-injection.sv(96) @ 0: uvm_test_top [get_max_size] path=prj_B.a1.addr max_size=16
UVM_INFO error-injection.sv(96) @ 0: uvm_test_top [get_max_size] path=prj_B.a1.b.cfg max_size=4
UVM_INFO error-injection.sv(96) @ 0: uvm_test_top [get_max_size] path=prj_B.a1.b.mode max_size=6
UVM_INFO error-injection.sv(108) @ 10: uvm_test_top [single_bit_error] Inject single bit error into bit=1 path=prj_B.a1.b.cfg idx=1
UVM_INFO error-injection.sv(114) @ 10: uvm_test_top [single_bit_error] Org val=0x0 Err val=0x2 New val=0x2
UVM_INFO error-injection.sv(108) @ 30: uvm_test_top [single_bit_error] Inject single bit error into bit=1 path=prj_B.a1.b.mode idx=2
UVM_INFO error-injection.sv(114) @ 30: uvm_test_top [single_bit_error] Org val=0x0 Err val=0x2 New val=0x2
Using a custom sample function for functional cove...
Inheritance of covergroups


No comments made yet. Be the first to submit a comment
Already Registered? Login Here
Saturday, 06 June 2020

You consent to our cookies if you continue to use our website. To know more about cookies, see our privacy policy. I accept cookies from this site.