1/*****************************************************************************
2
3  Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
4  more contributor license agreements.  See the NOTICE file distributed
5  with this work for additional information regarding copyright ownership.
6  Accellera licenses this file to you under the Apache License, Version 2.0
7  (the "License"); you may not use this file except in compliance with the
8  License.  You may obtain a copy of the License at
9
10    http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15  implied.  See the License for the specific language governing
16  permissions and limitations under the License.
17
18 *****************************************************************************/
19
20//====================================================================
21//  Nov 06, 2008
22//
23//  Updated by:
24//    Xiaopeng Qiu, JEDA Technologies, Inc
25//    Email:  qiuxp@jedatechnologies.net
26//
27//  To fix violations of TLM2.0 rules, which are detected by JEDA
28//  TLM2.0 checker.
29//
30//====================================================================
31
32#ifndef __SIMPLE_AT_INITIATOR1_H__
33#define __SIMPLE_AT_INITIATOR1_H__
34
35#include "tlm.h"
36#include "tlm_utils/simple_initiator_socket.h"
37//#include <systemc>
38#include <cassert>
39#include <queue>
40//#include <iostream>
41
42class SimpleATInitiator1 : public sc_core::sc_module
43{
44public:
45  typedef tlm::tlm_generic_payload                  transaction_type;
46  typedef tlm::tlm_phase                            phase_type;
47  typedef tlm::tlm_sync_enum                        sync_enum_type;
48  typedef tlm_utils::simple_initiator_socket<SimpleATInitiator1> initiator_socket_type;
49
50public:
51  // extended transaction, holds tlm_generic_payload + data storage
52  template <typename DT>
53  class MyTransaction : public transaction_type
54  {
55  public:
56    MyTransaction()
57    {
58      this->set_data_ptr(reinterpret_cast<unsigned char*>(&mData));
59    }
60    MyTransaction(tlm::tlm_mm_interface* mm) : transaction_type(mm)
61    {
62      this->set_data_ptr(reinterpret_cast<unsigned char*>(&mData));
63    }
64
65    void setData(DT& data) { mData = data; }
66    DT getData() const { return mData; }
67
68  private:
69    DT mData;
70  };
71  typedef MyTransaction<unsigned int>  mytransaction_type;
72
73  // Dummy Transaction Pool
74  class SimplePool : public tlm::tlm_mm_interface
75  {
76  public:
77    SimplePool() {}
78    mytransaction_type* claim()
79    {
80      mytransaction_type* t = new mytransaction_type(this);
81      t->acquire();
82      return t;
83    }
84    void release(mytransaction_type* t)
85    {
86      t->release();
87    }
88    void free(tlm::tlm_generic_payload* t)
89    {
90      t->reset();
91      delete t;
92    }
93  };
94
95public:
96  initiator_socket_type socket;
97
98public:
99  SC_HAS_PROCESS(SimpleATInitiator1);
100  SimpleATInitiator1(sc_core::sc_module_name name,
101                  unsigned int nrOfTransactions = 0x5,
102                  unsigned int baseAddress = 0x0) :
103    sc_core::sc_module(name),
104    socket("socket"),
105    ACCEPT_DELAY(10, sc_core::SC_NS),
106    mNrOfTransactions(nrOfTransactions),
107    mBaseAddress(baseAddress),
108    mTransactionCount(0),
109    mCurrentTransaction(0)
110  {
111    // register nb_transport method
112    socket.register_nb_transport_bw(this, &SimpleATInitiator1::myNBTransport);
113
114    // Initiator thread
115    SC_THREAD(run);
116
117    SC_METHOD(endResponse)
118    sensitive << mEndResponseEvent;
119    dont_initialize();
120  }
121
122  bool initTransaction(mytransaction_type*& trans)
123  {
124    if (mTransactionCount < mNrOfTransactions) {
125      trans = transPool.claim();
126      trans->set_address(mBaseAddress + 4*mTransactionCount);
127      trans->setData(mTransactionCount);
128      trans->set_command(tlm::TLM_WRITE_COMMAND);
129
130    } else if (mTransactionCount < 2 * mNrOfTransactions) {
131      trans = transPool.claim();
132      trans->set_address(mBaseAddress + 4*(mTransactionCount - mNrOfTransactions));
133      trans->set_command(tlm::TLM_READ_COMMAND);
134
135    } else {
136      return false;
137    }
138
139    trans->set_data_length(4);
140    trans->set_streaming_width(4);
141
142    ++mTransactionCount;
143    return true;
144  }
145
146  void logStartTransation(mytransaction_type& trans)
147  {
148    if (trans.get_command() == tlm::TLM_WRITE_COMMAND) {
149      std::cout << name() << ": Send write request: A = 0x"
150                << std::hex << (unsigned int)trans.get_address()
151                << ", D = 0x" << trans.getData() << std::dec
152                << " @ " << sc_core::sc_time_stamp() << std::endl;
153
154    } else {
155      std::cout << name() << ": Send read request: A = 0x"
156                << std::hex << (int)trans.get_address() << std::dec
157                << " @ " << sc_core::sc_time_stamp() << std::endl;
158    }
159  }
160
161  void logEndTransaction(mytransaction_type& trans)
162  {
163    if (trans.get_response_status() != tlm::TLM_OK_RESPONSE) {
164      std::cout << name() << ": Received error response @ "
165                << sc_core::sc_time_stamp() << std::endl;
166
167    } else {
168      std::cout << name() <<  ": Received ok response";
169      if (trans.get_command() == tlm::TLM_READ_COMMAND) {
170        std::cout << ": D = 0x" << trans.getData() << std::dec;
171      }
172      std::cout << " @ " << sc_core::sc_time_stamp() << std::endl;
173    }
174  }
175
176  //
177  // Simple AT Initiator
178  // - Request must be accepted by the target before the next request can be
179  //   send
180  // - Responses can come out of order
181  // - Responses will be accepted after fixed delay
182  //
183  void run()
184  {
185    phase_type phase;
186    sc_core::sc_time t;
187
188    mytransaction_type* ptrans;
189    while (initTransaction(ptrans)) {
190      // Create transaction and initialise phase and t
191      mytransaction_type& trans = *ptrans;
192      phase = tlm::BEGIN_REQ;
193      t = sc_core::SC_ZERO_TIME;
194
195      logStartTransation(trans);
196
197      switch (socket->nb_transport_fw(trans, phase, t)) {
198      case tlm::TLM_COMPLETED:
199        // Transaction Finished, wait for the returned delay
200        wait(t);
201        logEndTransaction(trans);
202        transPool.release(&trans);
203        break;
204
205      case tlm::TLM_ACCEPTED:
206      case tlm::TLM_UPDATED:
207        switch (phase) {
208        case tlm::BEGIN_REQ:
209          // Request phase not yet finished
210          // Wait until end of request phase before sending new request
211
212          // FIXME
213          mCurrentTransaction = &trans;
214          wait(mEndRequestPhase);
215          mCurrentTransaction = 0;
216          break;
217
218        case tlm::END_REQ:
219          // Request phase ended
220          if (t != sc_core::SC_ZERO_TIME) {
221            // Wait until end of request time before sending new request
222            wait(t);
223          }
224          break;
225
226        case tlm::BEGIN_RESP:
227          // Request phase ended and response phase already started
228          if (t != sc_core::SC_ZERO_TIME) {
229            // Wait until end of request time before sending new request
230            wait(t);
231          }
232          if (mEndResponseQueue.empty()) {
233            // Notify end of response phase after ACCEPT delay
234            mEndResponseEvent.notify(ACCEPT_DELAY);
235          }
236          mEndResponseQueue.push(&trans);
237          break;
238
239        case tlm::END_RESP:   // fall-through
240        default:
241          // A target should never return with these phases
242          // If phase == END_RESP, nb_transport should have returned true
243          assert(0); exit(1);
244          break;
245        }
246        break;
247
248      default:
249        assert(0); exit(1);
250      };
251    }
252    wait();
253  }
254
255  sync_enum_type myNBTransport(transaction_type& trans, phase_type& phase, sc_core::sc_time& t)
256  {
257    switch (phase) {
258    case tlm::END_REQ:
259      assert(t == sc_core::SC_ZERO_TIME); // FIXME: can t != 0?
260      // Request phase ended
261      mEndRequestPhase.notify(sc_core::SC_ZERO_TIME);
262      return tlm::TLM_ACCEPTED;
263
264    case tlm::BEGIN_RESP:
265    {
266      assert(t == sc_core::SC_ZERO_TIME); // FIXME: can t != 0?
267
268      // Notify end of request phase if run thread is waiting for it
269      // FIXME
270      if (&trans == mCurrentTransaction) {
271        mEndRequestPhase.notify(sc_core::SC_ZERO_TIME);
272      }
273
274      assert(dynamic_cast<mytransaction_type*>(&trans));
275      mytransaction_type* myTrans = static_cast<mytransaction_type*>(&trans);
276      assert(myTrans);
277
278      if (mEndResponseQueue.empty()) {
279        // Notify end of response phase after ACCEPT delay
280        mEndResponseEvent.notify(ACCEPT_DELAY);
281      }
282      mEndResponseQueue.push(myTrans);
283      return tlm::TLM_ACCEPTED;
284    }
285
286    case tlm::BEGIN_REQ: // fall-through
287    case tlm::END_RESP:  // fall-through
288    default:
289      // A target should never call nb_transport with these phases
290      assert(0); exit(1);
291//      return tlm::TLM_COMPLETED;  //unreachable code
292    };
293  }
294
295  void endResponse()
296  {
297    assert(!mEndResponseQueue.empty());
298    // end response phase
299    phase_type phase = tlm::END_RESP;
300    sc_core::sc_time t = sc_core::SC_ZERO_TIME;
301    mytransaction_type* trans = mEndResponseQueue.front();
302    assert(trans);
303    mEndResponseQueue.pop();
304    #if ( ! NDEBUG )
305    sync_enum_type r = socket->nb_transport_fw(*trans, phase, t);
306    #endif /* ! NDEBUG */
307    assert(r == tlm::TLM_COMPLETED); // FIXME: target should return TLM_COMPLETED?
308    assert(t == sc_core::SC_ZERO_TIME); // t must be SC_ZERO_TIME
309
310    logEndTransaction(*trans);
311    transPool.release(trans);
312
313    if (!mEndResponseQueue.empty()) {
314      // Notify end of response phase after ACCEPT delay
315      mEndResponseEvent.notify(ACCEPT_DELAY);
316    }
317  }
318
319private:
320  const sc_core::sc_time ACCEPT_DELAY;
321
322private:
323  unsigned int mNrOfTransactions;
324  unsigned int mBaseAddress;
325  SimplePool transPool;
326  unsigned int mTransactionCount;
327  sc_core::sc_event mEndRequestPhase;
328  std::queue<mytransaction_type*> mEndResponseQueue;
329  sc_core::sc_event mEndResponseEvent;
330  transaction_type* mCurrentTransaction;
331};
332
333#endif
334