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 
31 template <int NR_OF_INITIATORS, int NR_OF_TARGETS>
32 class SimpleBusAT : public sc_core::sc_module
33 {
34 public:
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 
41 public:
42   target_socket_type target_socket[NR_OF_INITIATORS];
43   initiator_socket_type initiator_socket[NR_OF_TARGETS];
44 
45 public:
46   SC_HAS_PROCESS(SimpleBusAT);
SimpleBusAT(sc_core::sc_module_name name)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 
getPortId(const sc_dt::uint64 & address)72   unsigned int getPortId(const sc_dt::uint64& address)
73   {
74     return (unsigned int)address >> 28;
75   }
76 
getAddressOffset(unsigned int portId)77   sc_dt::uint64 getAddressOffset(unsigned int portId)
78   {
79     return portId << 28;
80   }
81 
getAddressMask(unsigned int portId)82   sc_dt::uint64 getAddressMask(unsigned int portId)
83   {
84     return 0xfffffff;
85   }
86 
decode(const sc_dt::uint64 & address)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 
RequestThread()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 
ResponseThread()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           sync_enum_type r = (*it->second.to)->nb_transport_fw(*trans, phase, t);
206           assert(r == tlm::TLM_COMPLETED); (void)r;
207         }
208 
209         mPendingTransactions.erase(it);
210         trans->release();
211       }
212     }
213   }
214 
215   //
216   // interface methods
217   //
218 
initiatorNBTransport(int initiator_id,transaction_type & trans,phase_type & phase,sc_core::sc_time & t)219   sync_enum_type initiatorNBTransport(int initiator_id,
220                                       transaction_type& trans,
221                                       phase_type& phase,
222                                       sc_core::sc_time& t)
223   {
224     if (phase == tlm::BEGIN_REQ) {
225       trans.acquire();
226       addPendingTransaction(trans, 0, initiator_id);
227 
228       mRequestPEQ.notify(trans, t);
229 
230     } else if (phase == tlm::END_RESP) {
231       mEndResponseEvent.notify(t);
232       return tlm::TLM_COMPLETED;
233 
234     } else {
235       std::cout << "ERROR: '" << name()
236                 << "': Illegal phase received from initiator." << std::endl;
237       assert(false); exit(1);
238     }
239 
240     return tlm::TLM_ACCEPTED;
241   }
242 
targetNBTransport(int portId,transaction_type & trans,phase_type & phase,sc_core::sc_time & t)243   sync_enum_type targetNBTransport(int portId,
244                                    transaction_type& trans,
245                                    phase_type& phase,
246                                    sc_core::sc_time& t)
247   {
248     if (phase != tlm::END_REQ && phase != tlm::BEGIN_RESP) {
249       std::cout << "ERROR: '" << name()
250                 << "': Illegal phase received from target." << std::endl;
251       assert(false); exit(1);
252     }
253 
254     mEndRequestEvent.notify(t);
255     if (phase == tlm::BEGIN_RESP) {
256       mResponsePEQ.notify(trans, t);
257     }
258 
259     return tlm::TLM_ACCEPTED;
260   }
261 
transportDebug(int initiator_id,transaction_type & trans)262   unsigned int transportDebug(int initiator_id, transaction_type& trans)
263   {
264     unsigned int portId = decode(trans.get_address());
265     assert(portId < NR_OF_TARGETS);
266     initiator_socket_type* decodeSocket = &initiator_socket[portId];
267     trans.set_address( trans.get_address() & getAddressMask(portId) );
268 
269     return (*decodeSocket)->transport_dbg(trans);
270   }
271 
limitRange(unsigned int portId,sc_dt::uint64 & low,sc_dt::uint64 & high)272   bool limitRange(unsigned int portId, sc_dt::uint64& low, sc_dt::uint64& high)
273   {
274     sc_dt::uint64 addressOffset = getAddressOffset(portId);
275     sc_dt::uint64 addressMask = getAddressMask(portId);
276 
277     if (low > addressMask) {
278       // Range does not overlap with addressrange for this target
279       return false;
280     }
281 
282     low += addressOffset;
283     if (high > addressMask) {
284       high = addressOffset + addressMask;
285 
286     } else {
287       high += addressOffset;
288     }
289     return true;
290   }
291 
getDMIPointer(int initiator_id,transaction_type & trans,tlm::tlm_dmi & dmi_data)292   bool getDMIPointer(int initiator_id,
293                      transaction_type& trans,
294                      tlm::tlm_dmi&  dmi_data)
295   {
296     // FIXME: DMI not supported for AT bus?
297     sc_dt::uint64 address = trans.get_address();
298 
299     unsigned int portId = decode(address);
300     assert(portId < NR_OF_TARGETS);
301     initiator_socket_type* decodeSocket = &initiator_socket[portId];
302     sc_dt::uint64 maskedAddress = address & getAddressMask(portId);
303 
304     trans.set_address(maskedAddress);
305 
306     bool result =
307       (*decodeSocket)->get_direct_mem_ptr(trans, dmi_data);
308 
309     if (result)
310     {
311       // Range must contain address
312       assert(dmi_data.get_start_address() <= maskedAddress);
313       assert(dmi_data.get_end_address() >= maskedAddress);
314     }
315 
316     // Should always succeed
317 	sc_dt::uint64 start, end;
318 	start = dmi_data.get_start_address();
319 	end = dmi_data.get_end_address();
320 
321 	limitRange(portId, start, end);
322 
323 	dmi_data.set_start_address(start);
324 	dmi_data.set_end_address(end);
325 
326     return result;
327   }
328 
invalidateDMIPointers(int portId,sc_dt::uint64 start_range,sc_dt::uint64 end_range)329   void invalidateDMIPointers(int portId,
330                              sc_dt::uint64 start_range,
331                              sc_dt::uint64 end_range)
332   {
333     // FIXME: probably faster to always invalidate everything?
334 
335     if ((portId >= 0) && !limitRange(portId, start_range, end_range)) {
336       // Range does not fall into address range of target
337       return;
338     }
339 
340     for (unsigned int i = 0; i < NR_OF_INITIATORS; ++i) {
341       (target_socket[i])->invalidate_direct_mem_ptr(start_range, end_range);
342     }
343   }
344 
345 private:
addPendingTransaction(transaction_type & trans,initiator_socket_type * to,int initiatorId)346   void addPendingTransaction(transaction_type& trans,
347                              initiator_socket_type* to,
348                              int initiatorId)
349   {
350     const ConnectionInfo info = { &target_socket[initiatorId], to };
351     assert(mPendingTransactions.find(&trans) == mPendingTransactions.end());
352     mPendingTransactions[&trans] = info;
353   }
354 
355 private:
356   struct ConnectionInfo {
357     target_socket_type* from;
358     initiator_socket_type* to;
359   };
360   typedef std::map<transaction_type*, ConnectionInfo> PendingTransactions;
361   typedef typename PendingTransactions::iterator PendingTransactionsIterator;
362   typedef typename PendingTransactions::const_iterator PendingTransactionsConstIterator;
363 
364 private:
365   PendingTransactions mPendingTransactions;
366 
367   tlm_utils::peq_with_get<transaction_type> mRequestPEQ;
368   sc_core::sc_event mBeginRequestEvent;
369   sc_core::sc_event mEndRequestEvent;
370 
371   tlm_utils::peq_with_get<transaction_type> mResponsePEQ;
372   sc_core::sc_event mBeginResponseEvent;
373   sc_core::sc_event mEndResponseEvent;
374 };
375 
376 #endif
377