Thursday, June 26, 2014

SystemVerilog Constraints from Above

After reading the title, some of you might be asking yourselves "What are constraints from above?". Constraints from above (CFAs) are an e term. As Reuven Naveh explains in this post on Team Specman's blog, CFAs have the following properties:

  • They constrain a do-not-generate field or its descendants.
  • The constraint is declared not in the type in which the field is declared, but at a higher level.
  • The field is later generated on-the-fly.

For those of you who opened the link, they look remarkably similar to in-line constraints, but in modern e CFAs are more complicated than what that article presents. We won't go into that as this is a SystemVerilog post.

Why are CFAs useful? To best answer that question, let's look at an example. Let's say we have a trivial sequence item that models accessing a communication device:

typedef enum { CONFIG, TX_SHORT, TX_LONG, RX_SHORT, RX_LONG, SHUTDOWN } mode_e;

class some_item extends uvm_sequence_item;
  `uvm_object_utils(some_item)
  
  rand mode_e mode;
  
  function new(string name = "some_item");
    super.new(name);
  endfunction // new

endclass // some_item

We also have some sequence that operates with these items:

class some_sequence extends uvm_sequence #(some_item);
  `uvm_object_utils(some_sequence)
  
  rand int unsigned num_items;
  
  constraint max_items_c {
    num_items >= 5;
    num_items <= 10;
  }
  
  function new(string name = "some_sequence");
    super.new(name);
  endfunction // new
  
  task body();
    start_item(req);
    if (!req.randomize() with { mode == CONFIG; })
      `uvm_error("RANDERR", "Randomization error")
    finish_item(req);

    // start num_items comms
    repeat (num_items) begin
      start_item(req);
      if (!req.randomize() with { mode inside { TX_SHORT, TX_LONG, RX_SHORT, RX_LONG }; })
        `uvm_error("RANDERR", "Randomization error")      
      finish_item(req);
    end

    start_item(req);
    if (!req.randomize() with { mode == SHUTDOWN; })
      `uvm_error("RANDERR", "Randomization error")
    finish_item(req);
  endtask // body
  
endclass // some_sequence

We need to send an initial configuration command to set up the device. We follow up with the main test, throwing a few communication commands at the DUT and end it all with a shutdown. Pretty easy stuff up to now. Next, what if we want to have another sequence that restricts our traffic to only short communication commands? This already hints at class inheritance because a short communication sequence is just a specialization of our current sequence.

What disturbs us is the inline constraint on req, because we hardcoded the values that we want our communication commands to take. The most naïve and inefficient approach would be to just re-implement the body() task. In this case it's not a big deal, because we don't do much aside from running our traffic, right? Aside from the traffic loop which we need to slightly change, It's just two little 4-line blocks we need to copy. But what happens if a new command gets added and we have to run that as well prior to starting our traffic? We would have a second place we have to patch. Not good.

What we could also do is spin-off our traffic loop to it's own method and override that. This could work, but it's so C++. The little e angel on my shoulder is telling me: "This would be a great time to use a constraint from above.".

Instead of hardcoding the constraint inside the with block, we can put a constraint on req in some_sequence's scope:

class some_sequence extends uvm_sequence #(some_item);
  // ...
  rand some_item req;
    
  constraint only_comms_c {
    req.mode inside { TX_SHORT, TX_LONG, RX_SHORT, RX_LONG };
  };
  
  // ...
endclass // some_sequence

Now, we don't want to randomize the whole sequence, because that would mess up the num_items field. Fortunately, SystemVerilog provides an easy way to randomize just a subset of an object's fields. The mechanism is called in-line random variable control. Here it is in action, randomizing just the req field:

class some_sequence extends uvm_sequence #(some_item);
  // ...
  
  task body();
    // ...

    // start num_items comms
    repeat (num_items) begin
      start_item(req);
      if (!this.randomize(req))
        `uvm_error("RANDERR", "Randomization error")
      req.set_item_context(this, get_sequencer);      
      finish_item(req);
    end
    
    // ...
  endtask // body
  
endclass // some_sequence

What is happening here is that we are randomizing inside some_sequence's scope.The solver will take all of the constraints defined in some_item together with the one we defined above on req.mode, but it will only update req. Think of it like disabling randomization for all fields except for req via calls to rand_mode(0).

For some reason, the call to randomize() allocates a new object, even though the standard explicitly states that "[randomize] does not allocate any class objects" (IEEE Std. 1800-2012). I've noticed this behavior on two different simulators and can't explain it (in a stripped down example this doesn't happen; the object is randomized in-place). This new sequence item doesn't have it's context (parent sequence and sequencer) set, hence the call to set_item_context(...).

If we want to create a sequence that starts only short communication commands sequences, it's enough to extend some_sequence and add another constraint saying that req should be either TX_SHORT or RX_SHORT:

class some_other_sequence extends some_sequence;
  `uvm_object_utils(some_other_sequence)

  function new(string name = "some_other_sequence");
    super.new(name);
  endfunction // new

  constraint only_short_c {
    req.mode inside { TX_SHORT, RX_SHORT };
  }
  
endclass // some_other_sequence

Short and sweet, just like in e (well, almost, if it weren't for all of that boilerplate code for factory registration and the trivial constructor).

We got points 2 and 3 of Reuven's quote down, but what about point 1? The cool thing about this approach is that it could also work for non-random fields, with the caveat that the semantics of constraints are slightly different for e than for SystemVerilog. We could just as well not tag req as random, but we would need to take care that it's initial value is legal with respect to the constraints (or we would need to tag the constraints as soft). This is because SystemVerilog checks that even constraints on just state variables also hold, whereas (I think) e discards them.

We could do crazy stuff like disable the constraint inside the constructor and re-enable it inside post_randomize(). This would ensure that when the initial randomization of the whole sequence was executed our CFA doesn't lead to a contradiction. After re-enabling the constraint we could randomize only req (also in post_randomize()) to protect ourselves from another randomization of the sequence. You know what? Too complicated, forget it. I'll gladly call what we did here a CFAs even though it doesn't apply to a state variable, if it makes things simpler.

As usual, the examples (and more test code) can be found on SourceForge. Feel free to download it and experiment. See you next time!

Thursday, June 19, 2014

Disabling Field Checks in vr_ad

Register packages are an awesome tool to easily implement checks on the behavior of our DUT by holding a copy of its state inside a register model. When everything is properly hooked up, writing to the DUT will update the state of this model, while reading from it will check that the output from the DUT matches to what we expect based on the register model's state.

Things are not as clearly cut as above, though. Aside from different field access policies with respect to what can be read or written, there are also register fields that we cannot easily check like this. Some fields are not only written/read from the outside, but the hardware itself can update them, which means we need to model this as well. What is sometimes the case, however, is that our model is not fully accurate.

For example, the fields of a status register will get updated based on the internal operation of the DUT, but we may not know the exact clock cycle when a status flag is supposed to be updated. This leads to a window of uncertainty when a read of that flag might be flagged as an error, because the hardware was updated, but our model was not. Another example is at SoC level, where we assume that all blocks are working correctly (because they are pre-verified at module level). For performance reasons we might not want to have all of the modeling logic running for fields that are updated by hardware.

What we need to do in such cases is disable the comparison of these fields. vr_ad provides a mechanism inside vr_ad_reg to control which fields are considered for checking based on a compare mask. The problem with this is that there is one compare mask for the whole register and it doesn't support per-field operations directly (individual bits can be excluded, but the user has to figure out which ones). In contrast, UVM RAL allows individual fields to be easily excluded from the comparison.

What we would like to have is a similar mechanism for vr_ad. The reason behind it is that hardcoding specific bits of the compare mask (corresponding to specific fields) to 0 will make our code inflexible should the offsets of these fields ever change. Let's start off slowly and gradually work our way up.

Let's suppose we have a register called SOME_REG that contains 3 fields which are both readable and writable (for simplicity).

<'
reg_def SOME_REG SOME_REG_FILE 0x0 {
  reg_fld FOO : uint(bits : 8);
  reg_fld BAR : uint(bits : 8);
  reg_fld QUX : uint(bits : 16);
};
'>

Let's say that field FOO is updated by hardware and we want to disable comparing it and take the value that we read from it as it is. To do that we can declare a method called disable_check_foo() and call it from set_static_info().

<'
extend SOME_REG vr_ad_reg {
  disable_foo_check() is {
    var fields_info := get_fields_info().all(.fld_name == "FOO");
    var cmask := get_compare_mask();
    var lo := fields_info[0].fld_fidx;
    var hi := lo + fields_info[0].fld_size - 1;
    cmask[hi:lo] = 0;
    set_compare_mask(cmask);
  };
  
  set_static_info() is also {
    disable_foo_check();
  };
};
'>

Every register contains a struct that holds information about its fields. We use this struct to get the offset and size of FOO and set the bits corresponding to it to 0 inside the compare mask.

We can test our new method by doing a compare_and_update(...) with a value different than the one we have in the register model.

<'
extend sys {
  run() is also {
    message(LOW, "Simulating update by hardware on field FOO with value 0xff");
    compute some_reg.compare_and_update(0x0, { 0x00; 0x00; 0x00; 0xff });
  };
};
'>

Without the call to disable_check_foo() the comparison would fail. Go ahead and try it out for yourself to see.

This is all fine and dandy, it does what it's supposed to, but what if we have multiple fields we want to disable? Copying the method and adapting it for each field is out of the question. Well, we can have the field name as a parameter.

<'
extend SOME_REG vr_ad_reg {
  disable_field_check(fld_name : string) is {
    var fields_info := get_fields_info();
    
    // essential to check that the field actually exists
    assert(fields_info.has(.fld_name == fld_name));
    
    fields_info = fields_info.all(.fld_name == fld_name);
    var cmask := get_compare_mask();
    var lo := fields_info[0].fld_fidx;
    var hi := lo + fields_info[0].fld_size - 1;
    cmask[hi:lo] = 0;
    set_compare_mask(cmask);
  };
  
  set_static_info() is also {
    disable_field_check("FOO");
    disable_field_check("QUX");
  };
};
'>

What we definitely need to make sure of is that the field exists in the register. We can't do this at compile time, so we have to add the assert to check at run-time. Let's give this one a test as well.

<'
extend sys {  
  run() is also {
    message(LOW, "Simulating update by hardware on field QUX with value 0xff");
    compute some_reg.compare_and_update(0x0, { 0xff; 0x00; 0x00; 0x00 });
  };
};
'>

Now the comparison for both FOO and QUX is disabled.

The next question that naturally comes to mind is "What if I have multiple registers whose fields I don't want to check?". What I first thought was "I'll just declare the disable_field_check(...) method inside sys or util in such a way that it takes the register instance and the field name as a parameter.". That's a bad idea; I've been doing too much SystemVerilog the past few months and it's skewed my thinking. We've got the power of AOP on our side so we can just declare disable_field_check(...) directly inside vr_ad_reg and use it in all of its subtypes.

<'
extend vr_ad_reg {
  disable_field_check(fld_name : string) is {
    // same implementation as above
    // ...
  };
};

extend SOME_REG vr_ad_reg {
  set_static_info() is also {
    disable_field_check("FOO");
  };
};

extend SOME_OTHER_REG vr_ad_reg {
  set_static_info() is also {
    disable_field_check("BAZ");
  };
};
'>

What have we achieved here? We can disable checking of any field in any register by just referring to the field's name. We don't have to jump to the spec and see where a specific field is located, then set the compare mask to 0 for those bits. By not hardcoding the bits to exclude in our verification environment we are also protected from future changes in field offsets.

The disable_field_check(...) method doesn't necessarily have to be called from set_static_info(). It can be called whenever and wherever during the simulation. It could also be paired with a function that re-enables checking to allow for time windows where specific fields are not checked. It would be nice if vr_ad incorporated this or something similar in a future release.

I've uploaded the code to SourceForge, if anyone wants to play with it hands-on. Until next time!

Sunday, June 15, 2014

Temporary Variables in SystemVerilog Procedural Blocks

It is a well known fact that inside a procedural block variables can only be defined at the very beginning. Say you would have the following code:

task some_task();
  // do some stuff
  // ...
  
  // want to do some stuff here, but need a new var
endtask

You're writing some task, execute a few procedural statements and at some point you see that you need to add  new variable. You have to define that new variable way at the top (as the following code sample shows), far away from the point where it will be used, which doesn't really help with code readability. I find this to be a very arbitrary limitation.

task some_task();
  int some_var; // defined here, but used way farther down
  // doing some stuff
  // ...
  
  // do some stuff here with 'some_var'
endtask

Well, if you just need a temporary, throw-away variable, then you can just declare it and the statements operating on it inside a new begin-end sequential block:

task some_task();
  // do some stuff
  // ...
  
  begin
    int some_var;
    //do some stuff here with 'some_var'
  end
  
  // carry on with other statements
  // ...
endtask

While this is by no means a groundbreaking idea, it may help keep your code more understandable. Happy coding!