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_INITIATOR2_H__
33#define __SIMPLE_AT_INITIATOR2_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 SimpleATInitiator2 : 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<SimpleATInitiator2>  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(SimpleATInitiator2);
100  SimpleATInitiator2(sc_core::sc_module_name name,
101                     unsigned int nrOfTransactions = 0x5,
102                     unsigned int baseAddress = 0) :
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, &SimpleATInitiator2::myNBTransport);
113
114    // Initiator thread
115    SC_THREAD(run);
116  }
117
118  bool initTransaction(mytransaction_type*& trans)
119  {
120    if (mTransactionCount < mNrOfTransactions) {
121      trans = transPool.claim();
122      trans->set_address(mBaseAddress + 4*mTransactionCount);
123      trans->setData(mTransactionCount);
124      trans->set_command(tlm::TLM_WRITE_COMMAND);
125
126    } else if (mTransactionCount < 2 * mNrOfTransactions) {
127      trans = transPool.claim();
128      trans->set_address(mBaseAddress + 4*(mTransactionCount - mNrOfTransactions));
129      trans->set_command(tlm::TLM_READ_COMMAND);
130
131    } else {
132      return false;
133    }
134
135    trans->set_data_length(4);
136    trans->set_streaming_width(4);
137
138    ++mTransactionCount;
139    return true;
140  }
141
142  void logStartTransation(mytransaction_type& trans)
143  {
144    if (trans.get_command() == tlm::TLM_WRITE_COMMAND) {
145      std::cout << name() << ": Send write request: A = 0x"
146                << std::hex << (unsigned int)trans.get_address()
147                << ", D = 0x" << trans.getData() << std::dec
148                << " @ " << sc_core::sc_time_stamp() << std::endl;
149
150    } else {
151      std::cout << name() << ": Send read request: A = 0x"
152                << std::hex << (unsigned int)trans.get_address() << std::dec
153                << " @ " << sc_core::sc_time_stamp() << std::endl;
154    }
155  }
156
157  void logEndTransaction(mytransaction_type& trans)
158  {
159    if (trans.get_response_status() != tlm::TLM_OK_RESPONSE) {
160      std::cout << name() << ": Received error response @ "
161                << sc_core::sc_time_stamp() << std::endl;
162
163    } else {
164      std::cout << name() <<  ": Received ok response";
165      if (trans.get_command() == tlm::TLM_READ_COMMAND) {
166        std::cout << ": D = 0x" << std::hex << trans.getData() << std::dec;
167      }
168      std::cout << " @ " << sc_core::sc_time_stamp() << std::endl;
169    }
170  }
171
172  //
173  // Simple AT Initiator
174  // - Request must be accepted by the target before the next request can be
175  //   send
176  // - Responses can come out of order
177  // - Responses will be accepted after fixed delay
178  //
179  void run()
180  {
181    phase_type phase;
182    sc_core::sc_time t;
183
184    mytransaction_type* ptrans;
185    while (initTransaction(ptrans)) {
186      // Create transaction and initialise phase and t
187      mytransaction_type& trans = *ptrans;
188      phase = tlm::BEGIN_REQ;
189      t = sc_core::SC_ZERO_TIME;
190
191      logStartTransation(trans);
192
193      switch (socket->nb_transport_fw(trans, phase, t)) {
194      case tlm::TLM_COMPLETED:
195        // Transaction Finished, wait for the returned delay
196        wait(t);
197        logEndTransaction(trans);
198        transPool.release(&trans);
199        break;
200
201      case tlm::TLM_ACCEPTED:
202      case tlm::TLM_UPDATED:
203        switch (phase) {
204        case tlm::BEGIN_REQ:
205          // Request phase not yet finished
206          // Wait until end of request phase before sending new request
207
208          // FIXME
209          mCurrentTransaction = &trans;
210          wait(mEndRequestPhase);
211	  mCurrentTransaction = 0;
212          break;
213
214        case tlm::END_REQ:
215          // Request phase ended
216          if (t != sc_core::SC_ZERO_TIME) {
217            // Wait until end of request time before sending new request
218            wait(t);
219          }
220          break;
221
222        case tlm::BEGIN_RESP:
223          // Request phase ended and response phase already started
224          if (t != sc_core::SC_ZERO_TIME) {
225            // Wait until end of request time before sending new request
226            wait(t);
227          }
228          // Notify end of response phase after ACCEPT delay
229          t += ACCEPT_DELAY;
230          phase = tlm::END_RESP;
231          socket->nb_transport_fw(trans, phase, t);
232          logEndTransaction(trans);
233          transPool.release(&trans);
234          break;
235
236        case tlm::END_RESP:   // fall-through
237        default:
238          // A target should never return with these phases
239          // If phase == END_RESP, nb_transport should have returned true
240          assert(0); exit(1);
241          break;
242        }
243        break;
244
245      default:
246        assert(0); exit(1);
247      };
248    }
249    wait();
250
251  }
252
253  sync_enum_type myNBTransport(transaction_type& trans, phase_type& phase, sc_core::sc_time& t)
254  {
255    switch (phase) {
256    case tlm::END_REQ:
257      assert(t == sc_core::SC_ZERO_TIME); // FIXME: can t != 0?
258      // Request phase ended
259      mEndRequestPhase.notify(sc_core::SC_ZERO_TIME);
260      return tlm::TLM_ACCEPTED;
261
262    case tlm::BEGIN_RESP:
263    {
264      assert(t == sc_core::SC_ZERO_TIME); // FIXME: can t != 0?
265
266      // Notify end of request phase if run thread is waiting for it
267      // FIXME
268      if (&trans == mCurrentTransaction) {
269        mEndRequestPhase.notify(sc_core::SC_ZERO_TIME);
270      }
271
272      assert(dynamic_cast<mytransaction_type*>(&trans));
273      mytransaction_type* myTrans = static_cast<mytransaction_type*>(&trans);
274      assert(myTrans);
275
276      // Notify end of response phase after ACCEPT delay
277      t += ACCEPT_DELAY;
278      phase = tlm::END_RESP;
279      logEndTransaction(*myTrans);
280      transPool.release(myTrans);
281
282      return tlm::TLM_COMPLETED;
283    }
284
285    case tlm::BEGIN_REQ: // fall-through
286    case tlm::END_RESP:  // fall-through
287    default:
288      // A target should never call nb_transport with these phases
289      assert(0); exit(1);
290//      return tlm::TLM_COMPLETED;  //unreachable code
291    };
292  }
293
294private:
295  const sc_core::sc_time ACCEPT_DELAY;
296
297private:
298  unsigned int mNrOfTransactions;
299  unsigned int mBaseAddress;
300  SimplePool transPool;
301  unsigned int mTransactionCount;
302  sc_core::sc_event mEndRequestPhase;
303  transaction_type* mCurrentTransaction;
304};
305
306#endif
307