UVM Tutorial for Candy Lovers – 11. Sequence Item Port

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.

Class Diagram Related to the Sequence Item Port

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.

Sequence Diagram
  • The UVM driver instantiates the seq_item_port (step 1), and the UVM sequencer instantiates the seq_item_export (step 2).
  • When seq_item_port.connect(jb_seqr.seq_item_export) is called, the seq_item_export is stored in the array called m_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 called m_imp_list (step 4). The function checks the number of implementations. If it is 1, the function stores the implementation as m_if (step 5). This is the m_if the sequence item port delegated its jobs to. If the number of implementations is 0, the seq_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 the get_next_item() of the seq_item_export, which in turn calls the get_next_item() of the uvm_sequencer (steps 6 and 7).
  • Similarly, 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 (steps 8 and 9).

I hope this post helped you to understand how the sequence item port works.

9 thoughts on “UVM Tutorial for Candy Lovers – 11. Sequence Item Port”

    1. Suppose we have a one_sour_jelly_bean_sequence class that extends the one_jelly_bean_sequence.

      class one_sour_jelly_bean_sequence extends one_jelly_bean_sequence;

      To override the type, you could do:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      
      // test.svh
       
      function void build_phase( uvm_phase phase );
        super.build_phase( phase );
       
        one_jelly_bean_sequence::type_id::set_type_override( one_sour_jelly_bean_sequence::get_type() );  // ...
      endfunction: build_phase
       
      task main_phase( uvm_phase phase );
        one_jelly_bean_sequence jb_seq;
       
        // jb_seq will be overriden by the one_sour_jelly_bean_sequence
       
        jb_seq = one_jelly_bean_sequence::type_id::create( "jb_seq" );
        // ...
      endtask: main

      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 a get_full_name() function of the component that creates the sequence.
      To override the instance, you could do:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      
      // test.svh
       
      function void build_phase( uvm_phase phase );
        super.build_phase( phase );
       
        one_jelly_bean_sequence::type_id::set_inst_override( one_sour_jelly_bean_sequence::get_type(),
          .inst_path( { get_full_name(), ".jb_seq" } ) ); // instance path="uvm_test_top.jb_seq"  // ...
      endfunction: build_phase
       
      task main_phase( uvm_phase phase );
        one_jelly_bean_sequence jb_seq;
       
        // jb_seq will be overriden by the one_sour_jelly_bean_sequence
       
        jb_seq = one_jelly_bean_sequence::type_id::create( "jb_seq", 
          .contxt( get_full_name() ) ); // context="uvm_test_top"; instance path="uvm_test_top.jb_seq"  // ...
      endtask: main
  1. 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?

    1. Technically you could directly connect a uvm_sequencer to a uvm_driver without using a port as you mentioned. Your m_comp should be the uvm_sequencer type, not the uvm_component type as it does not implement the get_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 of uvm_sqr_if_base class.

  2. 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.

  3. 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

Leave a Reply