Àâòîáèîãðàôèÿ
Ðåôåðàò
Áèáëèîòåêà
Ññûëêè
Îò÷åò î ïîèñêå
Èíäèâèäóàëüíîå çàäàíèå

Transaction Level Modeling in SystemC

Adam Rose, Mentor Graphics; Stuart Swan, John Pierce, Jean-Michel Fernandez, Cadence Design Systems

Èñòî÷íèê: http://www.systemc.org/ (Òðåáóåòñÿ ðåãèñòðàèöÿ)

ABSTRACT

In the introduction, we describe the motivation for proposing a Transaction Level Modeling standard, focusing on the main use cases and the increase in productivity such a standard will bring. In Section 2, we describe the core tlm proposal in detail. Section 3 shows refinement of a single master / single slave from a programmers view model down through various levels of abstraction to an rtl only implementation. Section 4 shows how to code commonly occurring System Level design patterns such as centralized routers, arbiters, pipelines, and decentralized decoding schemes using the standard. Section 5 shows how to combine and recombine the generic components in section 4 to explore different switch architectures.

In the first Appendix, we outline the uses of sc_export, which relied on in many of the examples. In the second Appendix, we briefly discuss some guidelines for using the OSCI TLM in a concurrent SystemC environment in an efficient and safe way. The final appendix provides a list of all the tlm interfaces.

Code for all the examples contained in this paper is available in the OSCI TLM kit available at www.systemc.org.

1. Introduction

Transaction Level Modeling ( TLM ) is motivated by a number of practical problems. These include:

  • Providing an early platform for software development

  • System Level Design Exploration and Verification

  • The need to use System Level Models in Block Level Verification.

A commonly accepted industry standard for TLM would help to increase the productivity of software engineers, architects, implementation and verification engineers. However, the improvement in productivity promised by such a standard can only be achieved if the standard meets a number of criteria:

  • It must be easy, efficient and safe to use in a concurrent environment.

  • It must enable reuse between projects and between abstraction levels within the same project.

  • It must easily model hardware, software and designs which cross the hardware / software boundary.

  • It must enable the design of generic components such as routers and arbiters.

Since the release of version 2.0, it has been possible to do TLM using SystemC. However, the lack of established standards and methodologies has meant that each TLM effort has had to invent its own methodologies and APIs to do TLM. In addition to the cost of reinventing the wheel, these methodologies all differed slightly, making IP exchange difficult.

This paper will describe how the proposed OSCI TLM standard meets the requirements above, and show how to use it to solve various common modeling problems. We believe that widespread adoption of this proposal will lead to the productivity improvements promised by TLM.

2. The TLM Proposal

2.1 Key Concepts

There are three key concepts required to understand this proposal.

  • Interfaces

  • Blocking vs Non Blocking

  • Bidirectional vs Uni Directional

2.1.1 Interfaces

The emphasis on interfaces rather than implementation flows from the fact that SystemC is a C++ class library, and that C++ ( when used properly ) is an object orientated language. First we need to rigorously define the key interfaces, and then we can go on to discuss the various ways these may be implemented in a TLM design. It is crucial for the reader to understand that the TLM interface classes form the heart of the TLM standard, and that the implementations of those interfaces (e.g. tlm_fifo) are not as central. In SystemC, all interfaces should inherit from the class sc_interface.

2.1.2 Blocking and Non Blocking

In SystemC, there are two basic kinds of processes: SC_THREAD and SC_METHOD. The key difference between the two is that it is possible to suspend an SC_THREAD by calling wait(.). SC_METHODs on the other hand can only be synchronized by making them sensitive to an externally defined sc_event. Calling wait(.) inside an SC_METHOD leads to a runtime error. Using SC_THREAD is in many ways more natural, but it is slower because wait(.) induces a context switch in the SystemC scheduler. Using SC_METHOD is more constrained but more efficient, because it avoids the context switching [2].

Because there will be a runtime error if we call wait from inside an SC_METHOD, every method in every interface needs to clearly tell the user whether it may contain a wait(.) and therefore must be called from an SC_THREAD, or if it is guaranteed not to contain a wait(.) and therefore can be called from an SC_METHOD. OSCI uses the terms blocking for the former and non blocking for the latter.

The OSCI TLM standard strictly adheres to the OSCI use of the terms “blocking” and “non-blocking”. For example, if a TLM interface is labeled “non-blocking”, then its methods can NEVER call wait().

OSCI Terminology Contains wait(.) Can be called from
Blocking Possibly SC_THREAD only
Non Blocking No SC_METHOD or SC_THREAD

2.1.3 Bidirectional and Unidirectional Transfers

Some common transactions are clearly bidirectional, for example a read across a bus. Other transactions are clearly unidirectional, as is the case for most packet based communication mechanisms. Where there is a more complicated protocol, it is always possible to break it down into a sequence of bidirectional or unidirectional transfers. For example, a complex bus with address, control and data phases may look like a simple bidirectional read/write bus at a high level of abstraction, but more like a sequence of pipelined unidirectional transfers at a more detailed level. Any TLM standard must have both bidirectional and unidirectional interfaces. The standard should have a common look and feel for bidirectional and unidirectional interfaces, and it should be clearly shown how the two relate.

2.2 The Core TLM Interfaces

2.2.1 The Unidirectional Interfaces

The unidirectional interfaces are based on the sc_fifo interfaces as standardized in the SystemC 2.1 release. Sc_fifo has been used for many years in many types of system level model, since the critical variable in many system level designs is the size of the fifos. As a result, the fifo interfaces are well understood and we know that they are reliable in the context of concurrent systems. A further advantage of using interfaces based on sc_fifo is that future simulators may be able to perform well known static scheduling optimizations on models which use them. In addition to this, the interface classes are split into blocking and non blocking classes and non blocking access methods are distinguished from blocking methods by the prefix “nb_”.

However, for TLM we have three new requirements

  • We need some value free terminology, since “read” and “write” in the current sc_fifo interfaces are very loaded terms in the context of TLM

  • These interfaces may be implemented in a fifo, some other channel, or directly in the target using sc_export.

  • We need a non consuming peek interface

To address the first of these concerns, when we move a transaction from initiator to target we call this a “put” and when we move the transaction from target to initiator we call this a “get”.

A consequence of the second requirement is that we need to add tlm_tag to some of the interfaces. This is a C++ trick which allows us to implement more than one version of an interface in a single target, provided the template parameters of the interfaces are different.

The third requirement is satisfied by introducing blocking, non blocking and combined peek interfaces.

2.2.2 The Unidirectional Blocking Interfaces

template < typename T >
class tlm_blocking_get_if :
public virtual sc_interface
{
public:
    virtual T get( tlm_tag *t = 0 ) = 0;
    virtual void get( T &t ) { t = get(); }
};

template < typename T >
class tlm_blocking_peek_if :
public virtual sc_interface
{
public:
    virtual T peek( tlm_tag *t = 0 ) = 0;
    virtual void peek( T &t ) { t = peek(); }
};

template < typename T >
class tlm_blocking_put_if :
public virtual sc_interface
{
public:
    virtual void put( const T &t ) = 0;
};

Since we are allowed to call wait in the blocking functions, they never fail. For convenience, we supply two forms of get and peek, although since we provide a default implementation for the pass-by-reference form, an implementer of the interface need only supply one.

2.2.3 The Unidirectional Non Blocking Interfaces

template < typename T >
class tlm_nonblocking_get_if :
public virtual sc_interface
{
public:
    virtual bool nb_get( T &t ) = 0;
    virtual bool nb_can_get( tlm_tag *t = 0 ) const = 0;
    virtual const sc_event &ok_to_get( tlm_tag *t = 0 ) const = 0;
};

template < typename T >
class tlm_nonblocking_get_if :
public virtual sc_interface
{
public:
    virtual bool nb_peek( T &t ) = 0;
    virtual bool nb_can_peek( tlm_tag *t = 0 ) const = 0;
    virtual const sc_event &ok_to_peek( tlm_tag *t = 0 ) const = 0;
};

template < typename T >
class tlm_nonblocking_put_if :
public virtual sc_interface
{
public:
    virtual bool nb_put( const T &t ) = 0;
    virtual bool nb_can_put( tlm_tag *t = 0 ) const = 0;
    virtual const sc_event &ok_to_put( tlm_tag *t = 0 ) const = 0;
};

The non blocking interfaces may fail, since they are not allowed to wait for the correct conditions for these calls to succeed. Hence nb_put, nb_get and nb_peek must return a bool to indicate whether the nonblocking access succeeded. We also supply nb_can_put, nb_can_get and nb_can_peek to enquire whether a transfer will be successful without actually moving any data.

These methods are sufficient to do polling puts, gets and peeks. We also supply event functions which enable an SC_THREAD to wait until it is likely that the access succeeds or a SC_METHOD to be woken up because the event has been notified. These event functions enable an interrupt driven approach to using the non blocking access functions. However, in the general case even if the relevant event has been notified, we still need to check the return value of the access function – for example, a number of threads may have been notified that a fifo is no longer full but only the first to wake up is guaranteed to have room before it is full again.

2.2.4 Bidirectional Blocking Interface

template
class tlm_transport_if : public sc_interface
{
public:
    virtual RSP transport(const REQ&) = 0;
};

The bidirectional blocking interface is used to model transactions where there is a tight one to one, non pipelined binding between the request going in and the response coming out. This is typically true when modeling from a software programmers point of view, when for example a read can be described as an address going in and the read data coming back.

The signature of the transport function can be seen as a merger between the blocking get and put functions. This is by design, since then we can produce implementations of tlm_transport_if which simply call the put(.) and get(.) of two unidirectional interfaces.

2.3 TLM Channels

One or more of the interfaces described above can be implemented in any channel that a user cares to design, or directly in the target using sc_export. However, three related channels seem to be useful in a large number of modeling contexts, so they are included as part of the core proposal.

2.3.1 tlm_fifo

The tlm_fifo templated class implements all the unidirectional interfaces described above. The implementation of the fifo is based on the implementation of sc_fifo. In particular, it addresses many ( but not all ) of the issues related to non determinism by using the request_update / update mechanism. Externally, the effect of this is that a transaction put into the tlm_fifo is not available for getting until the next delta cycle. In addition to the functionality provided by sc_fifo, tlm_fifo can be zero or infinite sized, and implements the fifo interface extensions discussed in 4.3.1 below.

2.3.2 tlm_req_rsp_channel

The tlm_req_rsp_channel class consists of two fifos, one for the request going from initiator to target and the other for the response being moved from target to initiator. To provide direct access to these fifos, it exports the put request and get response interfaces to the initiator and the get request and put response interfaces to the target.

For convenience, these are grouped into master and slave interfaces as shown below :

template < typename REQ , typename RSP >
class tlm_master_if :
    public virtual tlm_extended_put_if< REQ > ,
    public virtual tlm_extended_get_if< RSP > {};

template < typename REQ , typename RSP >
class tlm_slave_if :
    public virtual tlm_extended_put_if< RSP > ,
    public virtual tlm_extended_get_if< REQ > {};
};

The fifos in tlm_req_rsp_channel can be of arbitrary size.

2.3.3 tlm_transport_channel

The tlm_transport_channel is used to model situations in which each request is tightly bound to one response. Because of this tight one to one binding, the request and response fifos must be of size one. As well as directly exporting the same interfaces exported by tlm_req_rsp_channel, tlm_transport_channel implements the bidirectional transport interface as shown below :

RSP transport( const REQ &req ) {
    RSP rsp;

    mutex.lock();

    request_fifo.put( req );
    response_fifo.get( rsp );

    mutex.unlock();
    return rsp;

}

This simple function provides a key link between the bidirectional and sequential world as represented by the transport function and the timed, unidirectional world as represented by tlm_fifo. We will explain this in detail in the transactor ( 3.4 ) and arbiter ( 4.2 ) examples below.

2.4 Summary of the Core TLM Proposal

The methods and classes described in Section 2.2 form the basis of the OSCI TLM proposal. On the basis of this simple transport mechanism, we can build models of software and hardware, generic routers and arbiters, pipelined and non pipelined buses, and packet based protocols. We can model at various different levels of timing and data abstraction and we can also provide channels to connect one abstraction level to another. Because they are based on the interfaces to sc_fifo, they are easily understood, safe and efficient.

Users can and should design their own channels implementing some or all of these interfaces, or they can implement them directly in the target using sc_export. The transport function in particular will often be directly implemented in a target when used to provide fast programmers view models for software prototyping.

In addition to the core interfaces, we have defined three standard channels, tlm_fifo, tlm_req_rsp_channel and tlm_transport_channel. These three channels can be used to model a wide variety of timed systems, with the tlm_transport_channel class providing an easy to use bridge between the untimed and timed domains.



Ìàãèñòð ÄîíÍÒÓ Ñìåøêîâ Àëåêñàíäð Ñåðãååâè÷