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_LT_INITIATOR3_DMI_H__
33#define __SIMPLE_LT_INITIATOR3_DMI_H__
34
35#include "tlm.h"
36#include "tlm_utils/simple_initiator_socket.h"
37#include <systemc>
38#include <cassert>
39#include <iostream>
40#include <map>
41
42class SimpleLTInitiator3_dmi : public sc_core::sc_module
43{
44public:
45  typedef tlm::tlm_generic_payload                       transaction_type;
46  typedef tlm::tlm_dmi                                   dmi_type;
47  typedef tlm::tlm_phase                                 phase_type;
48  typedef tlm::tlm_sync_enum                             sync_enum_type;
49  typedef tlm_utils::simple_initiator_socket<SimpleLTInitiator3_dmi>  initiator_socket_type;
50
51public:
52  initiator_socket_type socket;
53
54public:
55  SC_HAS_PROCESS(SimpleLTInitiator3_dmi);
56  SimpleLTInitiator3_dmi(sc_core::sc_module_name name,
57                  unsigned int nrOfTransactions = 0x5,
58                  unsigned int baseAddress = 0x0) :
59    sc_core::sc_module(name),
60    socket("socket"),
61    mNrOfTransactions(nrOfTransactions),
62    mBaseAddress(baseAddress),
63    mTransactionCount(0)
64  {
65    mDMIDataReads.first.set_start_address(1);
66    mDMIDataReads.first.set_end_address(0);
67    mDMIDataWrites.first.set_start_address(1);
68    mDMIDataWrites.first.set_end_address(0);
69
70    socket.register_invalidate_direct_mem_ptr(this, &SimpleLTInitiator3_dmi::invalidate_direct_mem_ptr);
71
72    // Initiator thread
73    SC_THREAD(run);
74  }
75
76  bool initTransaction(transaction_type& trans)
77  {
78    if (mTransactionCount < mNrOfTransactions) {
79      trans.set_address(mBaseAddress + 4*mTransactionCount);
80      mData = mTransactionCount;
81      trans.set_command(tlm::TLM_WRITE_COMMAND);
82
83    } else if (mTransactionCount < 2 * mNrOfTransactions) {
84      trans.set_address(mBaseAddress + 4*(mTransactionCount-mNrOfTransactions));
85      mData = 0;
86      trans.set_command(tlm::TLM_READ_COMMAND);
87
88    } else {
89      return false;
90    }
91
92    trans.set_data_ptr(reinterpret_cast<unsigned char*>(&mData));
93    trans.set_data_length(4);
94    trans.set_streaming_width(4);
95    trans.set_dmi_allowed(false);
96    trans.set_response_status(tlm::TLM_INCOMPLETE_RESPONSE);
97
98    ++mTransactionCount;
99    return true;
100  }
101
102  void logStartTransation(transaction_type& trans)
103  {
104    if (trans.get_command() == tlm::TLM_WRITE_COMMAND) {
105      std::cout << name() << ": Send write request: A = 0x"
106                << std::hex << (unsigned int)trans.get_address()
107                << ", D = 0x" << mData << std::dec
108                << " @ " << sc_core::sc_time_stamp() << std::endl;
109
110    } else {
111      std::cout << name() << ": Send read request: A = 0x"
112                << std::hex << (unsigned int)trans.get_address() << std::dec
113                << " @ " << sc_core::sc_time_stamp() << std::endl;
114    }
115  }
116
117  void logEndTransaction(transaction_type& trans)
118  {
119    if (trans.get_response_status() != tlm::TLM_OK_RESPONSE) {
120      std::cout << name() << ": Received error response @ "
121                << sc_core::sc_time_stamp() << std::endl;
122
123    } else {
124      std::cout << name() <<  ": Received ok response";
125      if (trans.get_command() == tlm::TLM_READ_COMMAND) {
126        std::cout << ": D = 0x" << std::hex << mData << std::dec;
127      }
128      std::cout << " @ " << sc_core::sc_time_stamp() << std::endl;
129    }
130  }
131
132  std::pair<dmi_type, bool>& getDMIData(const transaction_type& trans)
133  {
134    if (trans.get_command() == tlm::TLM_READ_COMMAND) {
135      return mDMIDataReads;
136
137    } else { // WRITE
138      return mDMIDataWrites;
139    }
140  }
141
142  void run()
143  {
144    transaction_type trans;
145    sc_core::sc_time t;
146
147    while (initTransaction(trans)) {
148      // Create transaction and initialise t
149      t = sc_core::SC_ZERO_TIME;
150
151      logStartTransation(trans);
152
153      ///////////////////////////////////////////////////////////
154      // DMI handling:
155      // We do *not* use the DMI hint to check if it makes sense to ask for
156      // DMI pointers. So the pattern is:
157      // - if the address is not covered by a DMI region try to acquire DMI
158      //   pointers
159      // - if we have a DMI pointer, do the DMI "transaction"
160      // - otherwise fall back to a normal transaction
161      ///////////////////////////////////////////////////////////
162
163      std::pair<dmi_type, bool>& dmi_data = getDMIData(trans);
164
165      // Check if we need to acquire a DMI pointer
166      if((trans.get_address() < dmi_data.first.get_start_address()) ||
167         (trans.get_address() > dmi_data.first.get_end_address()) )
168      {
169          sc_dt::uint64 address = trans.get_address(); //save original address
170          dmi_data.second =
171            socket->get_direct_mem_ptr(trans,
172                                       dmi_data.first);
173          trans.set_address(address);
174      }
175      // Do DMI "transaction" if we have a valid region
176      if (dmi_data.second &&
177          (trans.get_address() >= dmi_data.first.get_start_address()) &&
178          (trans.get_address() <= dmi_data.first.get_end_address()) )
179      {
180          // We can handle the data here. As the logEndTransaction is assuming
181          // something to happen in the data structure, we really need to
182          // do this:
183          trans.set_response_status(tlm::TLM_OK_RESPONSE);
184
185          sc_dt::uint64 tmp = trans.get_address() - dmi_data.first.get_start_address();
186          if (trans.get_command() == tlm::TLM_WRITE_COMMAND)
187          {
188              *(unsigned int*)&dmi_data.first.get_dmi_ptr()[tmp] = mData;
189          }
190          else
191          {
192              mData = *(unsigned int*)&dmi_data.first.get_dmi_ptr()[tmp];
193          }
194
195          // Do the wait immediately. Note that doing the wait here eats almost
196          // all the performance anyway, so we only gain something if we're
197          // using temporal decoupling.
198          if (trans.get_command() == tlm::TLM_WRITE_COMMAND) {
199            wait(dmi_data.first.get_write_latency());
200
201          } else {
202            wait(dmi_data.first.get_read_latency());
203          }
204      }
205      else // we need a full transaction
206      {
207          socket->b_transport(trans, t);
208          // wait for the returned delay
209          wait(t);
210      }
211
212      logEndTransaction(trans);
213    }
214    wait();
215
216  }
217
218  // Invalidate DMI pointer(s)
219  void invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
220                                 sc_dt::uint64 end_range)
221  {
222    // FIXME: probably faster to always invalidate everything?
223    if (start_range <= mDMIDataReads.first.get_end_address ()&&
224        end_range >= mDMIDataReads.first.get_start_address()) {
225        mDMIDataReads.second = false;
226    }
227    if (start_range <= mDMIDataWrites.first.get_end_address ()&&
228        end_range >= mDMIDataWrites.first.get_start_address()) {
229      mDMIDataWrites.second = false;
230    }
231  }
232
233private:
234  std::pair<dmi_type, bool> mDMIDataReads;
235  std::pair<dmi_type, bool> mDMIDataWrites;
236
237  sc_core::sc_event mEndEvent;
238  unsigned int mNrOfTransactions;
239  unsigned int mBaseAddress;
240  unsigned int mTransactionCount;
241  unsigned int mData;
242};
243
244#endif
245