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#ifndef __SIMPLEBUSAT_H__
21#define __SIMPLEBUSAT_H__
22
23//#include <systemc>
24#include "tlm.h"
25
26#include "tlm_utils/simple_target_socket.h"
27#include "tlm_utils/simple_initiator_socket.h"
28
29#include "tlm_utils/peq_with_get.h"
30
31template <int NR_OF_INITIATORS, int NR_OF_TARGETS>
32class SimpleBusAT : public sc_core::sc_module
33{
34public:
35  typedef tlm::tlm_generic_payload               transaction_type;
36  typedef tlm::tlm_phase                         phase_type;
37  typedef tlm::tlm_sync_enum                     sync_enum_type;
38  typedef tlm_utils::simple_target_socket_tagged<SimpleBusAT>    target_socket_type;
39  typedef tlm_utils::simple_initiator_socket_tagged<SimpleBusAT> initiator_socket_type;
40
41public:
42  target_socket_type target_socket[NR_OF_INITIATORS];
43  initiator_socket_type initiator_socket[NR_OF_TARGETS];
44
45public:
46  SC_HAS_PROCESS(SimpleBusAT);
47  SimpleBusAT(sc_core::sc_module_name name) :
48    sc_core::sc_module(name),
49    mRequestPEQ("requestPEQ"),
50    mResponsePEQ("responsePEQ")
51  {
52     for (unsigned int i = 0; i < NR_OF_INITIATORS; ++i) {
53       target_socket[i].register_nb_transport_fw(this, &SimpleBusAT::initiatorNBTransport, i);
54       target_socket[i].register_transport_dbg(this, &SimpleBusAT::transportDebug, i);
55       target_socket[i].register_get_direct_mem_ptr(this, &SimpleBusAT::getDMIPointer, i);
56     }
57     for (unsigned int i = 0; i < NR_OF_TARGETS; ++i) {
58       initiator_socket[i].register_nb_transport_bw(this, &SimpleBusAT::targetNBTransport, i);
59       initiator_socket[i].register_invalidate_direct_mem_ptr(this, &SimpleBusAT::invalidateDMIPointers, i);
60     }
61
62     SC_THREAD(RequestThread);
63     SC_THREAD(ResponseThread);
64  }
65
66  //
67  // Dummy decoder:
68  // - address[31-28]: portId
69  // - address[27-0]: masked address
70  //
71
72  unsigned int getPortId(const sc_dt::uint64& address)
73  {
74    return (unsigned int)address >> 28;
75  }
76
77  sc_dt::uint64 getAddressOffset(unsigned int portId)
78  {
79    return portId << 28;
80  }
81
82  sc_dt::uint64 getAddressMask(unsigned int portId)
83  {
84    return 0xfffffff;
85  }
86
87  unsigned int decode(const sc_dt::uint64& address)
88  {
89    // decode address:
90    // - return initiator socket id
91
92    return getPortId(address);
93  }
94
95  //
96  // AT protocol
97  //
98
99  void RequestThread()
100  {
101    while (true) {
102      wait(mRequestPEQ.get_event());
103
104      transaction_type* trans;
105      while ((trans = mRequestPEQ.get_next_transaction())!=0) {
106        unsigned int portId = decode(trans->get_address());
107        assert(portId < NR_OF_TARGETS);
108        initiator_socket_type* decodeSocket = &initiator_socket[portId];
109        trans->set_address(trans->get_address() & getAddressMask(portId));
110
111        // Fill in the destination port
112        PendingTransactionsIterator it = mPendingTransactions.find(trans);
113        assert(it != mPendingTransactions.end());
114        it->second.to = decodeSocket;
115
116        phase_type phase = tlm::BEGIN_REQ;
117        sc_core::sc_time t = sc_core::SC_ZERO_TIME;
118
119        // FIXME: No limitation on number of pending transactions
120        //        All targets (that return false) must support multiple transactions
121        switch ((*decodeSocket)->nb_transport_fw(*trans, phase, t)) {
122        case tlm::TLM_ACCEPTED:
123        case tlm::TLM_UPDATED:
124          // Transaction not yet finished
125          if (phase == tlm::BEGIN_REQ) {
126            // Request phase not yet finished
127            wait(mEndRequestEvent);
128
129          } else if (phase == tlm::END_REQ) {
130            // Request phase finished, but response phase not yet started
131            wait(t);
132
133          } else if (phase == tlm::BEGIN_RESP) {
134            mResponsePEQ.notify(*trans, t);
135            // Not needed to send END_REQ to initiator
136            continue;
137
138          } else { // END_RESP
139            assert(0); exit(1);
140          }
141
142          // only send END_REQ to initiator if BEGIN_RESP was not already send
143          if (it->second.from) {
144            phase = tlm::END_REQ;
145            t = sc_core::SC_ZERO_TIME;
146            (*it->second.from)->nb_transport_bw(*trans, phase, t);
147          }
148
149          break;
150
151        case tlm::TLM_COMPLETED:
152          // Transaction finished
153          mResponsePEQ.notify(*trans, t);
154
155          // reset to destination port (we must not send END_RESP to target)
156          it->second.to = 0;
157
158          wait(t);
159          break;
160
161        default:
162          assert(0); exit(1);
163        };
164      }
165    }
166  }
167
168  void ResponseThread()
169  {
170    while (true) {
171      wait(mResponsePEQ.get_event());
172
173      transaction_type* trans;
174      while ((trans = mResponsePEQ.get_next_transaction())!=0) {
175        PendingTransactionsIterator it = mPendingTransactions.find(trans);
176        assert(it != mPendingTransactions.end());
177
178        phase_type phase = tlm::BEGIN_RESP;
179        sc_core::sc_time t = sc_core::SC_ZERO_TIME;
180
181        target_socket_type* initiatorSocket = it->second.from;
182        // if BEGIN_RESP is send first we don't have to send END_REQ anymore
183        it->second.from = 0;
184
185        switch ((*initiatorSocket)->nb_transport_bw(*trans, phase, t)) {
186        case tlm::TLM_COMPLETED:
187          // Transaction finished
188          wait(t);
189          break;
190
191        case tlm::TLM_ACCEPTED:
192        case tlm::TLM_UPDATED:
193          // Transaction not yet finished
194          wait(mEndResponseEvent);
195          break;
196
197        default:
198          assert(0); exit(1);
199        };
200
201        // forward END_RESP to target
202        if (it->second.to) {
203          phase = tlm::END_RESP;
204          t = sc_core::SC_ZERO_TIME;
205          #if ( ! NDEBUG )
206          sync_enum_type r = (*it->second.to)->nb_transport_fw(*trans, phase, t);
207          #endif /* ! NDEBUG */
208          assert(r == tlm::TLM_COMPLETED);
209        }
210
211        mPendingTransactions.erase(it);
212        trans->release();
213      }
214    }
215  }
216
217  //
218  // interface methods
219  //
220
221  sync_enum_type initiatorNBTransport(int initiator_id,
222                                      transaction_type& trans,
223                                      phase_type& phase,
224                                      sc_core::sc_time& t)
225  {
226    if (phase == tlm::BEGIN_REQ) {
227      trans.acquire();
228      addPendingTransaction(trans, 0, initiator_id);
229
230      mRequestPEQ.notify(trans, t);
231
232    } else if (phase == tlm::END_RESP) {
233      mEndResponseEvent.notify(t);
234      return tlm::TLM_COMPLETED;
235
236    } else {
237      std::cout << "ERROR: '" << name()
238                << "': Illegal phase received from initiator." << std::endl;
239      assert(false); exit(1);
240    }
241
242    return tlm::TLM_ACCEPTED;
243  }
244
245  sync_enum_type targetNBTransport(int portId,
246                                   transaction_type& trans,
247                                   phase_type& phase,
248                                   sc_core::sc_time& t)
249  {
250    if (phase != tlm::END_REQ && phase != tlm::BEGIN_RESP) {
251      std::cout << "ERROR: '" << name()
252                << "': Illegal phase received from target." << std::endl;
253      assert(false); exit(1);
254    }
255
256    mEndRequestEvent.notify(t);
257    if (phase == tlm::BEGIN_RESP) {
258      mResponsePEQ.notify(trans, t);
259    }
260
261    return tlm::TLM_ACCEPTED;
262  }
263
264  unsigned int transportDebug(int initiator_id, transaction_type& trans)
265  {
266    unsigned int portId = decode(trans.get_address());
267    assert(portId < NR_OF_TARGETS);
268    initiator_socket_type* decodeSocket = &initiator_socket[portId];
269    trans.set_address( trans.get_address() & getAddressMask(portId) );
270
271    return (*decodeSocket)->transport_dbg(trans);
272  }
273
274  bool limitRange(unsigned int portId, sc_dt::uint64& low, sc_dt::uint64& high)
275  {
276    sc_dt::uint64 addressOffset = getAddressOffset(portId);
277    sc_dt::uint64 addressMask = getAddressMask(portId);
278
279    if (low > addressMask) {
280      // Range does not overlap with addressrange for this target
281      return false;
282    }
283
284    low += addressOffset;
285    if (high > addressMask) {
286      high = addressOffset + addressMask;
287
288    } else {
289      high += addressOffset;
290    }
291    return true;
292  }
293
294  bool getDMIPointer(int initiator_id,
295                     transaction_type& trans,
296                     tlm::tlm_dmi&  dmi_data)
297  {
298    // FIXME: DMI not supported for AT bus?
299    sc_dt::uint64 address = trans.get_address();
300
301    unsigned int portId = decode(address);
302    assert(portId < NR_OF_TARGETS);
303    initiator_socket_type* decodeSocket = &initiator_socket[portId];
304    sc_dt::uint64 maskedAddress = address & getAddressMask(portId);
305
306    trans.set_address(maskedAddress);
307
308    bool result =
309      (*decodeSocket)->get_direct_mem_ptr(trans, dmi_data);
310
311    if (result)
312    {
313      // Range must contain address
314      assert(dmi_data.get_start_address() <= maskedAddress);
315      assert(dmi_data.get_end_address() >= maskedAddress);
316    }
317
318    // Should always succeed
319	sc_dt::uint64 start, end;
320	start = dmi_data.get_start_address();
321	end = dmi_data.get_end_address();
322
323	limitRange(portId, start, end);
324
325	dmi_data.set_start_address(start);
326	dmi_data.set_end_address(end);
327
328    return result;
329  }
330
331  void invalidateDMIPointers(int portId,
332                             sc_dt::uint64 start_range,
333                             sc_dt::uint64 end_range)
334  {
335    // FIXME: probably faster to always invalidate everything?
336
337    if ((portId >= 0) && !limitRange(portId, start_range, end_range)) {
338      // Range does not fall into address range of target
339      return;
340    }
341
342    for (unsigned int i = 0; i < NR_OF_INITIATORS; ++i) {
343      (target_socket[i])->invalidate_direct_mem_ptr(start_range, end_range);
344    }
345  }
346
347private:
348  void addPendingTransaction(transaction_type& trans,
349                             initiator_socket_type* to,
350                             int initiatorId)
351  {
352    const ConnectionInfo info = { &target_socket[initiatorId], to };
353    assert(mPendingTransactions.find(&trans) == mPendingTransactions.end());
354    mPendingTransactions[&trans] = info;
355  }
356
357private:
358  struct ConnectionInfo {
359    target_socket_type* from;
360    initiator_socket_type* to;
361  };
362  typedef std::map<transaction_type*, ConnectionInfo> PendingTransactions;
363  typedef typename PendingTransactions::iterator PendingTransactionsIterator;
364  typedef typename PendingTransactions::const_iterator PendingTransactionsConstIterator;
365
366private:
367  PendingTransactions mPendingTransactions;
368
369  tlm_utils::peq_with_get<transaction_type> mRequestPEQ;
370  sc_core::sc_event mBeginRequestEvent;
371  sc_core::sc_event mEndRequestEvent;
372
373  tlm_utils::peq_with_get<transaction_type> mResponsePEQ;
374  sc_core::sc_event mBeginResponseEvent;
375  sc_core::sc_event mEndResponseEvent;
376};
377
378#endif
379