Last Updated: September 11, 2013
A UVM driver and a UVM sequencer are connected using a UVM sequence item port and an export. This post will explain how the sequence item port works. In Agent, we connected the sequence item port (seq_item_port
) of the jelly-bean driver (jb_drvr
) to the sequence item export (seq_item_export
) of the jelly-bean sequencer (jb_seqr
) like this:
jb_drvr.seq_item_port.connect( jb_seqr.seq_item_export ); |
The driver called get_next_item()
to get a jelly-bean transaction (jb_tx
),
seq_item_port.get_next_item( jb_tx ); |
and then called item_done()
at the end.
seq_item_port.item_done(); |
We will look at how the above code works in detail in this post. The class diagram related to the sequence item port is shown below. UVM standard library classes are shown in pink, and the UVM classes specialized with the jelly_bean_transaction
type are shown in yellow.
Sequence Item Port
The seq_item_port
of the jelly-bean driver is an object of the uvm_seq_item_pull_port
class specialized with the jelly_bean_transaction
type. The uvm_seq_item_pull_port
class is defined in src/tlm1/uvm_sqr_connections.svh
and consists of two macros:
`UVM_SEQ_PORT
`UVM_SEQ_ITEM_PULL_IMP
The following pseudo code shows how these macros are expanded.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | class uvm_seq_item_pull_port #( type REQ = jelly_bean_transaction, type RSP = REQ ) extends uvm_port_base #( uvm_sqr_if_base #(REQ, RSP) ); // `UVM_SEQ_PORT( `UVM_SEQ_ITEM_PULL_MASK, "uvm_seq_item_pull_port" ) // | | // | V // | ( `UVM_SEQ_ITEM_GET_NEXT_ITEM_MASK | // | `UVM_SEQ_ITEM_TRY_NEXT_ITEM_MASK | // | `UVM_SEQ_ITEM_ITEM_DONE_MASK | // | `UVM_SEQ_ITEM_HAS_DO_AVAILABLE_MASK | // | `UVM_SEQ_ITEM_WAIT_FOR_SEQUENCES_MASK | // | `UVM_SEQ_ITEM_PUT_RESPONSE_MASK | // | `UVM_SEQ_ITEM_PUT_MASK | // | `UVM_SEQ_ITEM_GET_MASK | // | `UVM_SEQ_ITEM_PEEK_MASK ) = 9'h1FF // V function new( string name, uvm_component parent, int min_size = 0, int max_size = 1 ); super.new( name, parent, UVM_PORT, min_size, max_size ); m_if_mask = 9'h1FF; endfunction // new // | // +--> `UVM_TLM_GET_TYPE_NAME( "uvm_seq_item_pull_port" ) virtual function string get_type_name(); return "uvm_seq_item_pull_port"; endfunction // get_type_name // `UVM_SEQ_ITEM_PULL_IMP( this.m_if, // | jelly_bean_transaction, // | jelly_bean_transaction, t, t ) // V task get_next_item( output jelly_bean_transaction t ); this.m_if.get_next_item( t ); endtask // get_next_item task try_next_item( output jelly_bean_transaction t ); this.m_if.try_next_item( t ); endtask // try_next_item function void item_done( input jelly_bean_transaction t = null ); this.m_if.item_done( t ); endfunction // item_done task wait_for_sequences(); this.m_if.wait_for_sequences(); endtask // wait_for_sequences function bit has_do_available(); return this.m_if.has_do_available(); endfunction // has_do_available function void put_response( input jelly_bean_transaction t ); this.m_if.put_response( t ); endfunction // put_response task get( output jelly_bean_transaction t ); this.m_if.get( t ); endtask // get task peek( output jelly_bean_transaction t ); this.m_if.peek( t ); endtask // peek task put( input jelly_bean_transaction t ); this.m_if.put( t ); endtask // put // end of macro expansion bit print_enabled; endclass // uvm_seq_item_pull_port |
As you have seen above, both the get_next_item()
and the item_done()
simply delegates their job to this.m_if
(lines 39 and 47). But what is this.m_if
? Before answering this question, let’s look at the other side of the connection.
Sequence Item Export
The seq_item_export
of the jelly-bean sequencer is an object of a uvm_seq_item_pull_imp
class specialized with the jelly_bean_transaction
type. The uvm_seq_item_pull_imp
class is also defined in the src/tlm1/uvm_sqr_connections.svh
and consists of two macros:
`UVM_IMP_COMMON
`UVM_SEQ_ITEM_PULL_IMP
The following pseudo code shows how these macros are expanded.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | class uvm_seq_item_pull_imp #( type REQ = jelly_bean_transaction, type RSP = REQ, type IMP = uvm_sequencer #( jelly_bean_transaction, jelly_bean_transaction ) ) extends uvm_port_base #( uvm_sqr_if_base #( REQ, RSP ) ); // `UVM_IMP_COMMON( `UVM_SEQ_ITEM_PULL_MASK, "uvm_seq_item_pull_imp", // | uvm_sequencer #( jelly_bean_transaction, // | jelly_bean_transaction ) ) // V local uvm_sequencer #( jelly_bean_transaction, jelly_bean_transaction ) m_imp; function new( string name, uvm_sequencer #( jelly_bean_transaction, jelly_bean_transaction ) imp ); super.new( name, imp, UVM_IMPLEMENTATION, 1, 1 ); m_imp = imp; m_if_mask = 9'h1FF; endfunction // new // | // +--> `UVM_TLM_GET_TYPE_NAME( "uvm_seq_item_pull_imp" ) virtual function string get_type_name(); return "uvm_seq_item_pull_imp"; endfunction // get_type_name // `UVM_SEQ_ITEM_PULL_IMP( m_imp, // | jelly_bean_transaction, jelly_bean_transaction, // | t, t ) // V task get_next_item( output jelly_bean_transaction t ); m_imp.get_next_item( t ); endtask // get_next_item task try_next_item( output jelly_bean_transaction t ); m_imp.try_next_item( t ); endtask // try_next_item function void item_done( input jelly_bean_transaction t = null ); m_imp.item_done( t ); endfunction // item_done task wait_for_sequences(); m_imp.wait_for_sequences(); endtask // wait_for_sequences function bit has_do_available(); return m_imp.has_do_available(); endfunction // has_do_available function void put_response( input jelly_bean_transaction t ); m_imp.put_response( t ); endfunction // put_response task get( output jelly_bean_transaction t ); m_imp.get( t ); endtask // get task peek( output jelly_bean_transaction t ); m_imp.peek( t ); endtask // peek task put( input jelly_bean_transaction t ); m_imp.put( t ); endtask // put endclass // uvm_seq_item_pull_imp |
Similar to the uvm_seq_item_pull_port
class, the uvm_seq_item_pull_imp
class has the get_next_item()
and the item_done()
methods, and they delegate their job to m_imp
. The m_imp
is the sequencer this sequence item export belongs to.
Connection
Now let’s connect the sequence item port and the sequence item export. The following sequence diagram shows the steps involved in the connection.
- The UVM driver instantiates the
seq_item_port
(step 1), and the UVM sequencer instantiates theseq_item_export
(step 2). - When
seq_item_port.connect(jb_seqr.seq_item_export)
is called, theseq_item_export
is stored in the array calledm_provided_by
(step 3) - Just before entering
end_of_elaboration_phase
,resolve_bindings()
function is called. This function traverses the port connection and stores “implementation” to the array calledm_imp_list
(step 4). The function checks the number of implementations. If it is 1, the function stores the implementation asm_if
(step 5). This is them_if
the sequence item port delegated its jobs to. If the number of implementations is 0, theseq_item_port
is left unconnected. If the number of implementations is more than 1, it is an error. - When the
seq_item_port.get_next_item()
is called, the task calls theget_next_item()
of theseq_item_export
, which in turn calls theget_next_item()
of theuvm_sequencer
(steps 6 and 7). - Similarly, when the
seq_item_port.item_done()
is called, the function calls theitem_done()
of theseq_item_export
, which in turn calls theitem_done()
of theuvm_sequencer
(steps 8 and 9).
I hope this post helped you to understand how the sequence item port works.
Nice work Keisuke Shimizu Sir.These posts are like 1s in thousand type.I really appreciates the efforts
Can any one please explain in detail about Sequence Overriding methods ie., by type and by instance.
Suppose we have a
one_sour_jelly_bean_sequence
class that extends theone_jelly_bean_sequence
.To override the type, you could do:
To override an instance, you need to specify the path of the instance (line 7). Since a
uvm_sequence
is not a component, you need to specify the context of the sequence when you instantiate it (line 17). The context can be any string, but we usually use the result of aget_full_name()
function of the component that creates the sequence.To override the instance, you could do:
This may be a silly question.
I am wondering why we can not connect driver and sequencer with out port, to implement get_next_item and item_done.
Like,
driver.connect(sequencer)
function connect(uvm_component m_comp);
m_comp = sequencer
endfunction
while we call
driver.get_next_item();
it will call m_comp.get_next_item
can you pl guide on this?
Technically you could directly connect a
uvm_sequencer
to auvm_driver
without using a port as you mentioned. Yourm_comp
should be theuvm_sequencer
type, not theuvm_component
type as it does not implement theget_next_item
task. However, this creates unnecessary dependency between the driver and the sequencer. If you use a port, you can connect any component to the driver as long as it implements the methods ofuvm_sqr_if_base
class.Can we connect sequencer port to driver port like below
jb_seqr.seq_item_export .connect(jb_drvr.seq_item_port);
Please give some info on this.
No, you cannot connect an export to a port (the argument of the
connect
function must be a provider). This figure shows all possible connections.Sir,
You explained, “when the seq_item_port.item_done() is called, the function calls the item_done() of the seq_item_export, which in turn calls the item_done() of the uvm_sequencer”. Can ypu please explain in details what happens in depth with flow diagram
Thanks in advance
The
seq_item_port.item_done()
eventually calls theitem_done
function of theuvm_sequencer
as follows: