draft for uvm

Origin of Sequence

 

The initial driver has raise_objection and drop_objection

 

And the driver(tr) contained in the middle of the two
 

Because if you write in this way, you have to change the code every time you want to send a different package, which is very error-prone and poor scalability.

 

Therefore, you can put the same code each time in the driver, and each time a different code can be separated into one

Gen_pkt function

 

But there will be problems in the process of using gen_pkt, because if you want to use the functions with the same name are gen_pkt, but the functions are different, you need to use virtual functions and overloads, reload gen_pkt as virtual, When you want to use this gen_pkt, you need to instantiate a new driver with an overloaded gen_pkt function, which is quite troublesome to think about.

The other is to write a function with a different name, but then the driver still needs to change the code for each test case to add the function with a different name, and we are discussing it to avoid this problem, so this is obviously not advisable of.

 

Sequence start and execution

 

The startup method of Sequence is

My_seq.start(sequencer);

or

用default_sequence

 

 

After the sequence is started, the body task of the sequence will be automatically executed, as well as pre_body and post_body tasks

These two are before and after the body is executed

 

 

Start of multiple sequce

In fact, multiple sequences can be started at the same time, but at this time, you need to consider the start time of these sequences. This is the arbitration mechanism of the sequence. There are many arbitration algorithms for the sequence. If you want to change the default arbitration algorithm, you need to use

env.i_agt.sqr.set_arbitration(SEQ_ARB_STRICT_FIFO);

arbitration: arbitration

Change it.

The sequencer's arbitration algorithm is SEQ_ARB_FIFO by default. It will strictly follow the first-in-first-out order, without considering priority. SEQ_ARB_WEIGHTED is a weighted arbitration; SEQ_ARB_RANDOM is selected completely at random; SEQ_ARB_STRICT_FIFO is strictly based on priority. When there are multiple sequences with the same priority, they are selected in the order of first-in first-out; SEQ_ARB_STRICT_RANDOM is strictly based on priority. Same

For a sequence of one priority, select randomly from the highest priority; SEQ_ARB_USER is a new arbitration algorithm that users can customize.

 

 

First, for transaction, it has the concept of priority

Want to operate on his priority, you need to use uvm_do_pri

Second, for sequence, it also has the concept of priority

Its priority is actually the priority of the transaction in it. When you set the priority of a sequence, you want to set the priority of all transactions in it.

 

 

 

Sequencer lock operation and grab operation

 

 

Lock is to allow a sequence to occupy the sequencer until it unlocks itself.

 

Like the lock operation, the grab operation is also used to temporarily own the sequencer, but the grab operation has a higher priority than the lock operation. lock request is inserted

The sequencer is the last of the arbitration queue. When it reaches it, all the arbitration requests before it have ended. The grab request is placed at the top of the sequencer arbitration queue.

Almost has the ownership of the sequencer as soon as it is issued:

 

 

 

 

Sequence validity

During arbitration, the sequencer will check the return result of the is_relevant function of the sequence. If it is 1, the sequence is valid, otherwise it is invalid. Therefore, the sequence can be invalidated by overloading the is_relevant function:

is_relevant: meaningful

Invalidation means that the sequencer will not execute the sequence unless it becomes valid.

 

 

After the Sequencer has sent all transactions, if there is an invalid sequence at this time, it will automatically call the wait_for_relevant function of the invalid sequence. This function must contain operations that make the invalid sequence valid again. Otherwise, the entire system will fall into an endless loop.

 

In normal use, the is_relevant function and wait_for_relevant function must be overloaded at the same time.

 

 

 

Sequence related macros

`uvm_do is the most foolish method for generating transactions containing random data

`uvm_do_with provides constraints on certain fields when randomizing

`uvm_do_pri is used to set the priority of the sequence

`uvm_do_on is used to pair sequencers with sequence. When there are multiple sequencers, this macro can work.

In this case, you can actually know what with, pri, and on mean. As long as there is with, it includes the function of with, and the other is the same, so you can see at a glance what this macro does.      

 

In addition to uvm_do, you can also use uvm_create and uvm_send to generate transaction

 

`uvm_create(my_transaction) generates a transaction

assert(my_transaction)

my_transaction.num = num_set;

`uvm_send(my_transaction)

 

In addition, there is also uvm_send_pri, the same as the previous usage

 

`uvm_rand_send series macro

my_transaction = new(“my_transaction“)

`uvm_rand_send(my_transaction)

 

 

The significance of the uvm_rand_send series of macros and uvm_send series of macros is mainly that if a transaction occupies a large amount of memory, then it is likely that the two transactions sent before and after will use the same memory, but the contents can be different, which saves memory.

 

In other words, when the memory occupied by transaction is relatively large, we should use uvm_send instead of uvm-do. In this way, the memory consumption is smaller. If the transaction itself is small, there is no need to worry too much about which one to use.

 

 

How does macro do these things?

 

Macro operation depends on two tasks

start_item

finish_item

 

`uvm_do macro operation, equivalent to

 

tr = new ("tr");

start_item(tr,100);

assert(tr.randomize())

finish_item(tr,100);

 

The second parameter of these two tasks is priority.

Must operate at the same time.

 

 

uvm_do related operations

 

The uvm_do macro encapsulates too many operation steps, so its flexibility is insufficient. In order to make up for these shortcomings, uvm provides which interface is used to expand uvm_do

 

pre_do

mid_do

post_do

The order of execution is

 

 

 

Nested sequence

Suppose, there are two sequences that generate crc error packets

Now to write a new sequence, it can alternately generate these two different crc error packets

 

If you simply integrate the code, it is easy to make mistakes when the code is relatively long.

To avoid errors, sequence can start another sequence in its own content

 

crc_seq cseq;

longcrc_seq lseq;

 

repeat(10) begin

       cseq = new(“cseq”);

       lseq = new(“lseq”);

       cseq.start(m_sequencer);

       lseq.start(m_sequencer);

 

end

m_sequencer is the pointer to the sequencer used by case0_sequence after startup

You can also use a more concise approach:

`uvm_do (cseq);

`uvm_do (lseq);

This is really easy to use.

 

 

Series of macros such as uvm_send can be used to manipulate sequence.

As mentioned above, only, the parameters of start_item and finish_item can only be transaction

 

Use rand type variable in sequence

rand  bit[47:0] ldmac;

Means that this field will be randomized when using randomize,

So when you use it

 

virtual task body();

my_transaction tr;

       `uvm_do_with(tr,{tr.crc_err==0;

tr.pload.size()==1500;

tr.dmac=ldmac;})

tr.print();

endtask

In this way, the dmac in transaction is the random value of ldmac

The above sequence can be used as a low-level sequence, and its role is to randomize the ldmac of the transaction

 

repeat(10) begin

       `uvm_do_with (lseq, {lseq.ldmac == 48'hFFFF;})

end

 

Any number of rand modifiers can be added to the sequence to regulate the transaction it generates. In fact, transaction and sequence have many similarities, they can both use the rand modifier, and both can be used in uvm_do.

 

One thing to pay attention to when using the transaction of the sequence specification itself, the rand variable name in the sequence cannot be the same as the variable name in the transaction, because if they are the same, the compiler will first look for this variable in the transaction when compiling the program. , If there is, then it no longer cares about the variables in the sequence.

 

Transaction type matching problem

 

A sequence can only generate one type of transaction. If another sequence wants to be started in this sequence, its transaction must be the transaction of the upper sequence or its subclass.

 

If you want to send multiple transactions in a sequence, then its parameters need to be changed to uvm_sequence_item, and at the same time, there must be corresponding processing in the driver.

 

Use of p_sequencer

The previous article mentioned a m_sequencer, this pointer is the pointer of the sequencer after the sequence is started. Its type is uvm_sequencer_base (the base class of uvm_sequencer)

Instead of the my_sequencer type.

 

When there is such a situation, I sequencer got a dmac value inside, want to sequence with it, that is, to access the member variables in the sequence which sequencer inside.

If you use m_sequencer, it will be very troublesome. See page 437 of the book pdf for details.

 

In the actual verification platform, sequencer member variables are used in many cases. Taking this into account, uvm has built a macro

uvm_declare_p_sequencer(SEQUENCER),

 

The essence of this macro is to declare a member variable of type SEQUENCER.

 

You can write like this in the sequence

`uvm_declare_p_sequencer(my_sequencer)

This sentence is equivalent to

my_sequencer p_sequencer;

After writing this sentence, uvm automatically converts m_sequencer to p_sequencer through cast, so P_sequencer.dmac can be used directly in the sequence to get the member variables in the sequencer of dmac

 

Derivation and inheritance of sequence

 

Since sequence is a class, it can also derive other sequences

 

For the derived sequence , if you want to access the member variables in the corresponding sequencer , you just need to declare p_sequencer with uvm_declare_p_sequence in base_sequence, because the member variables declared by the base class can be used by its subclasses.

The use of virtual sequence

 

 

If, a dut has more than one interface, but two or more interfaces.

At this time, you need to instantiate another env to test another interface, and at the same time add a set of my_if, set multiple default sequences to apply excitation to the two data ports.

 

In the new verification platform, there are two drivers. If we want to send a long packet to driver0 first, on this basis, driver1 will start sending packets. How can we do that?

 

 

The first is to use global variables, but I don't like to give this example, it is easy to be biased. In fact, the problem cannot be solved fundamentally. For details, see uvm actual combat book pdf page447

 

In order to solve this problem, uvm provides a virtual sequence, I call him virtual sequence.

Its role is to unify the scheduling sequence, this function is also quite powerful. Powerful

This function is used in the use of the most common register model.

 

 

First, before creating a virtual sequence, you need to create a virtual sequence r

 

Very simple, we can use a picture to see the difference between using or not using virtual sequence

 

In the virtual sequence, you can use uvm_do_on to schedule and send the transaction

 

`uvm_declare_p_sequence(my_vsqr);

The p_sqr0 is the function of the virtual sequencer created first

 

Here seq0 and seq1 do not need to do anything, just need to be the same as the most common seq, and the scheduling work has been given to vseq.

 

The work of starting the sequence can also be started manually instead of `uvm_do_on. One advantage of manual starting is that you can pass some values ​​in it.

 

 

 

 

 

 

 

 

 

 

 

 

 

Guess you like

Origin blog.csdn.net/qq_41034231/article/details/108504683