0
votes

I have a testbench where I have two sequences: sequenceA and sequenceB with their corresponding sequence items (sqitemA and sqitemB). sequenceA is input to my DUT and randomly generates the values for sqitemA.

Now, I need to access some of these generated random fields in sqItemA to create a related sqItemB for my sequenceB.

Does UVM provide a mechanism to do this?

For example:

class sequenceA extends uvm_sequence;
  rand logic[31:0] address;
  rand bit         enable;

  // skipping constructor 

  task body;
      this.randomize();
      // do body stuff
  endtask
endclass

class sequenceB extends uvm_sequence;
  rand logic[31:0] address;
  rand bit         enable;

  // skipping constructor

  task body;
      // here I want this.address to match the address randomly generated in sequenceA
      // wait_till_sequenceA randomization is complete
      // this.address = sequenceA.address (??);
      // do body stuff
  endtask
endclass

Any clue on best way to do this?

2

2 Answers

2
votes

If you want to synchronize traffic across multiple sequences, your best bet is using a virtual sequence:

class virtual_seq extend uvm_sequence;
  sequence_a seq_a;
  sequence_b seq_b;

  `uvm_declare_p_sequencer(virtual_sequencer)

  task body();
    // create sequence A
    // ...

    // start sequence A on appropriate sequencer
    fork
      seq_a.start(p_sequencer.seqr_a);
    join_none

    // wait until seq_a's item finishes
    // 'end_event' is built into uvm_transaction and is trigger
    // when the driver calls item_done()
    seq_a.seq_item_a.end_event.wait_trigger();

    // create sequence B based on seq_a.seq_item_a
    // ...

    // start sequence B
    fork
      seq_b.start(p_sequencer.seqr_b);
    join_none
  endtask
endclass

The virtual_sequencer class contains handles of both bus sequencers. We've implemented synchronization here by letting sequence A do an item and only then start sequence B. Notice that sequence A runs on while we do this, since we haven't killed it. You can implement any kind of synchronization here, like grabbing seqr_a to pause sequence A until a certain point in sequence B's execution, etc. To get more details have a look in the UVM user guide.

If you want to wait only until the seq_a created and randomized its seq_item_a, you'll have to define a hook event inside it:

class sequence_a extends uvm_sequence #(sequence_item_a);
  event item_randomized;

  task body();
    // create 'seq_item_a'
    // ...

    seq_item_a.randomize();
    -> item_randomized;
  endtask
endclass

In the virtual sequence code, instead of waiting on end_event, just wait on item_randomized.

You can also make the item_randomized event part of the sequence item itself and trigger it from post_randomize():

class sequence_item_a extends uvm_sequence_item;
  event item_randomized;

  function post_randomize();
    -> item_randomized;
  endfunction
endclass
0
votes

The proper way to do this would be to remove the randomize part from the sequence body (if possible) and randomizing it before the seq.start method shown in Tudor's answer.

class virtual_seq extend uvm_sequence;
  sequence_a seq_a;
  sequence_b seq_b;

  `uvm_declare_p_sequencer(virtual_sequencer)

  task body();
    // create sequences
    // ...

     // randomize seq A
     seq_a.randomize();
     //randomize seq  B 
     seq_b.randomize() with {address = seq_A.address;} 
     // start sequences
     // ...

  endtask
endclass