John Aynsley, CTO, Doulos
In previous posts I described the TLM ports and exports introduced with VMM 1.2. Of course, the vmm_channel still remains one of the primary communication mechanisms in VMM. In this article I explore how TLM ports can be used with VMM channels.
TLM ports and exports support a style of communication between transactors in which a b_transport method made from a producer is implemented within a consumer, thereby allowing a transaction to be passed directly from producer to consumer without any intervening channel. On the other hand, a vmm_channel sits as a buffer between a producer and a consumer, with the producer putting transactions into the tail of the channel and the consumer getting transactions from the head of the channel. The channel deliberately isolates the producer from the consumer.
Each style of communication has its advantages. The b_transport style of communication is fast, direct, and the end-of-life of the transaction is handled independently from the contents and behavior of the transaction object. On the other hand, vmm_channel provides a lot more functionality and flexibility, including the ability to process transactions while they remain in the channel and to support a range of synchronization models between producer and consumer. In VMM 1.2, support for TLM ports and exports is now built into the vmm_channel class. It is possible to bind a TLM port to a VMM channel such that the channel provides the implementation of b_transport. The goal is to get the best of both worlds: the clean semantics of a b_transport call in the producer, and the convenience of using the active slot in the vmm_channel in the consumer. An example is shown below. First we need a transaction class and a corresponding channel type:
class my_tx extends vmm_data; // User-defined transaction …
typedef vmm_channel_typed #(my_tx) my_channel;
The producer sends transactions using a b_transport call, knowing that by the time the call returns, the transaction will have been completed:
class producer extends vmm_xactor;
vmm_tlm_b_transport_port #(producer, my_tx) m_port; … my_tx tx; … m_port.b_transport(tx, delay); …
The consumer manipulates the transaction while leaving it in the active slot of a vmm_channel and executing the standard notifications:
class consumer extends vmm_xactor;
my_channel m_chan; … my_tx tx; … m_chan.activate(tx); // Peek at the transaction in the channel m_chan.start(); // Notification vmm_data::STARTED … m_chan.complete(); // Notification vmm_data::ENDED m_chan.remove(); // Unblock the producer …
The channel is instantiated and connected in the surrounding environment:
class tb_env extends vmm_group; … my_channel m_tx_chan; producer m_producer; consumer m_consumer;
virtual function void build_ph;
m_producer = new( "m_producer", this ); m_consumer = new( "m_consumer", this );
m_tx_chan = new( "my_channel", "m_tx_chan" ); m_tx_chan.reconfigure(1);
endfunction
function void connect_ph();
vmm_connect #(.D(my_tx))::tlm_bind( m_tx_chan, m_producer.m_port, vmm_tlm::TLM_BLOCKING_EXPORT ); m_consumer.m_chan = m_tx_chan; …
There are two key points to note in the above. Firstly, the channel is reconfigured to have a full level of 1. This ensures that the blocking transport call does indeed block. If the full level is greater than 1, the first call to b_transport will return immediately before the transaction has completed, which would defeat the purpose.
Secondly, the transport port and the vmm_channel are bound together using the vmm_connect utility. This connect utility must be used when binding VMM TLM objects to channels, and can also used in order to bind TLM ports and exports of differing interface types (e.g. a blocking port to a non-blocking export). The third argument to tlm_bind indicates that the connection is being made from the port in the producer to a blocking export within the channel. I will discuss other uses for this method in later posts.
ShareThis
Posted in