haykp
haykp

Reputation: 445

How to generate ascending values during randomisation

Please help to resolve one randomization-constraint related issue that I am facing.

So in my seqItem, I have a write_addr random variable. This variable controls the location in memory where the data should be written. I want to implement different writing address changing modes like random-address, given range address, ascending and descending type. I have params_pkg, where user defines the address change type and my TB generates write_addr values correspondingly.

I was thinking to implement this using constraints, like by enableing/disabling the constrains get the required behavioral:

class seqItem extends uvm_sequence_item;
    `uvm_object_param_utils(seqItem)

    randc logic [541-1:515] wfifo_addr;
   if (params_pkg::writeAddressType == "WriteGivenRangeAddress") begin
        constraint wArrdGivnRangCnstr {
            this.wfifo_addr  inside {[params_pkg::addrLowValue:params_pkg::addrHighValue]};
       }
 end
  function new (string name="seqItem");
        super.new(name);
        this.wArrdGivnRangCnstr.constraint_mode(0);
    endfunction

endclass

However there is no way to generate ascending or descending address values using constraints. Because to have ascending address, the seqitem code needs to know the write_addr variable value from the previous randomization, which I could not implement.

My question is: whether it is possible to have ascending write_addr values using constraints?

And second, the example code that I posted is not working, simulator gives error saying that generate constraints are not allowed. Most probably this is something not supported in System Verilog. Am I right?

Upvotes: 1

Views: 3737

Answers (3)

Matthew
Matthew

Reputation: 13987

One solution to your problem of changing randomisation modes, is to turn constraints on and off:

You can turn a constraint off using the implicit constraint_mode method:

s.wArrdGivnRangCnstr.constraint_mode(0);

and then turn it on again:

s.wArrdGivnRangCnstr.constraint_mode(1);

(where s is a reference to your seqItem class). You can't put constraints inside an if statement as your error message demonstrates.

Upvotes: 0

Paul S
Paul S

Reputation: 7755

Second part first: I suspect "Generate constraints" are constraints contained within a generate block. Generate blocks are only allowed within modules, programs, and checkers not classes, hence "Generate constraints" are illegal (although the term is oddly specific. I'd expect an error saying "Generates are not allowed in classes"). You can rewrite your constraints by moving the conditional inside the constraint block:

constraint wArrdGivnRangCnstr {
    if (params_pkg::writeAddressType == "WriteGivenRangeAddress") {
        this.wfifo_addr inside {[params_pkg::addrLowValue:params_pkg::addrHighValue]
    };
}

BTW: you might want to consider an enum for the writeAddressType variable. That way typos are caught at compile time.


Another thing...

You have your random variable defined as randc.

Variables declared with the randc keyword are random-cyclic variables that cycle through all the values in a random permutation of their declared range.

If you limit the range on a randc variable, it can't "cycle through all the values...of [it's] declared range". It's not clear from the SystemVerilog LRM what will happen here, but I suspect that once all the values between low and high are exhausted randomisation will fail.

Also, the variable is 26-bits in size. That's 67,108,864 different values you're asking the simulator to keep track of to see if they've been used before. It will need 8MB of flags just for this one variable.

I expect what you really want here is to define the variable as rand and not randc.


On to your main question...

You are right, you need some kind of storage of the last value in order to get incrementing values, and because this is a sequence item I suspect that you're creating a new instance each time, hence we can't store the last value in an instance variable as all instance variables are destroyed.

So there's two options:

  1. Store the last value in a static instance variable.

    class seqItem extends uvm_sequence_item;
        `uvm_object_param_utils(seqItem)
    
        rand logic [541-1:515] write_addr;
        static logic [541-1:515] last_write_addr = 0;
        constraint wAddrIncr {
            write_addr > last_write_addr;
        }
        function new (string name="seqItem");
            super.new(name);
        endfunction
    
        function post_randomize();
            last_write_add = write_addr;
        endfunction
    endclass
    
  2. Add a constraint in the sequence when randomising the sequence item

    class someSequence extends uvm_sequence;
    
        ...stuff omitted...
    
        task body();
            seqItem item;
            seqItem last_item;
    
            last_item = null;
    
            repeat (4728346) begin
                item = new(); // or create to use factory
                if (last_item) begin
                    item.randomize() with {
                        write_addr > last_item.write_addr
                    };
                end else begin
                    item.randomize();
                end
                last_item = item;
    
                // Send to driver or whatever
            end
        endtask
    endclass
    

Number 2 is better in my opinion, because it doesn't bake the increasing address behaviour into the sequence item. Incrementing addresses is really a property of the sequence of items, not of any single item. I can now write some sequences that have increasing addresses, decreasing addresses, or any other pattern.

Upvotes: 3

Matthew
Matthew

Reputation: 13987

One solution to your problem of generating ascending (or descending) addresses is to keep a note of the last value generated and to use this as the lower bound in the constraint:

  class seqItem;

    randc logic [0:15] wfifo_addr;
    static logic [0:15] last_wfifo_addr = '0;

    constraint wArrdGivnRangCnstr {
      this.wfifo_addr  inside {[last_wfifo_addr:params_pkg::addrHighValue]};
    }

    function void post_randomize;
      last_wfifo_addr = wfifo_addr;
      if (last_wfifo_addr >= params_pkg::addrHighValue)
        last_wfifo_addr= params_pkg::addrLowValue;
    endfunction

  endclass

http://www.edaplayground.com/x/3QxX

The post_randomize function is a built-in method that can be overrided. It is called implicitly after the (built-in) randomize method. There is also a built-in pre_randomize, which of course you can override, too.

Upvotes: 0

Related Questions