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_INITIATOR2_DMI_H__
33#define __SIMPLE_LT_INITIATOR2_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 <iomanip>
41#include <map>
42
43class SimpleLTInitiator2_dmi : public sc_core::sc_module
44{
45public:
46  typedef tlm::tlm_generic_payload                      transaction_type;
47  typedef tlm::tlm_dmi                                  dmi_type;
48  typedef tlm::tlm_phase                                phase_type;
49  typedef tlm::tlm_sync_enum                            sync_enum_type;
50  typedef tlm_utils::simple_initiator_socket<SimpleLTInitiator2_dmi> initiator_socket_type;
51
52public:
53  initiator_socket_type socket;
54
55public:
56  SC_HAS_PROCESS(SimpleLTInitiator2_dmi);
57  SimpleLTInitiator2_dmi(sc_core::sc_module_name name,
58                  unsigned int nrOfTransactions = 0x5,
59                  unsigned int baseAddress = 0x0) :
60    sc_core::sc_module(name),
61    socket("socket"),
62    mNrOfTransactions(nrOfTransactions),
63    mBaseAddress(baseAddress),
64    mTransactionCount(0)
65  {
66    mDMIDataReads.first.set_start_address(1);
67    mDMIDataReads.first.set_end_address(0);
68    mDMIDataWrites.first.set_start_address(1);
69    mDMIDataWrites.first.set_end_address(0);
70
71    // register invalidate method
72    socket.register_invalidate_direct_mem_ptr(this, &SimpleLTInitiator2_dmi::invalidate_direct_mem_ptr);
73
74    // Initiator thread
75    SC_THREAD(run);
76  }
77
78  bool initTransaction(transaction_type& trans)
79  {
80    if (mTransactionCount < mNrOfTransactions) {
81      trans.set_address(mBaseAddress + 4*mTransactionCount);
82      mData = mTransactionCount;
83      trans.set_command(tlm::TLM_WRITE_COMMAND);
84
85    } else if (mTransactionCount < 2 * mNrOfTransactions) {
86      trans.set_address(mBaseAddress + 4*(mTransactionCount-mNrOfTransactions));
87      mData = 0;
88      trans.set_command(tlm::TLM_READ_COMMAND);
89
90    } else {
91      return false;
92    }
93
94    trans.set_data_ptr(reinterpret_cast<unsigned char*>(&mData));
95    trans.set_data_length(4);
96    trans.set_streaming_width(4);
97    trans.set_dmi_allowed(false);
98    trans.set_response_status(tlm::TLM_INCOMPLETE_RESPONSE);
99
100    ++mTransactionCount;
101    return true;
102  }
103
104  void logStartTransation(transaction_type& trans)
105  {
106    if (trans.get_command() == tlm::TLM_WRITE_COMMAND) {
107      std::cout << name() << ": Send write request: A = 0x"
108                << std::hex << (unsigned int)trans.get_address()
109                << ", D = 0x" << mData << std::dec
110                << " @ " << sc_core::sc_time_stamp() << std::endl;
111
112    } else {
113      std::cout << name() << ": Send read request: A = 0x"
114                << std::hex << (unsigned int)trans.get_address() << std::dec
115                << " @ " << sc_core::sc_time_stamp() << std::endl;
116    }
117  }
118
119  void logEndTransaction(transaction_type& trans)
120  {
121    if (trans.get_response_status() != tlm::TLM_OK_RESPONSE) {
122      std::cout << name() << ": Received error response @ "
123                << sc_core::sc_time_stamp() << std::endl;
124
125    } else {
126      std::cout << name() <<  ": Received ok response";
127      if (trans.get_command() == tlm::TLM_READ_COMMAND) {
128        std::cout << ": D = 0x" << std::hex << mData << std::dec;
129      }
130      std::cout << " @ " << sc_core::sc_time_stamp() << std::endl;
131    }
132  }
133
134  std::pair<dmi_type, bool>& getDMIData(const transaction_type& trans)
135  {
136    if (trans.get_command() == tlm::TLM_READ_COMMAND) {
137      return mDMIDataReads;
138
139    } else { // WRITE
140      return mDMIDataWrites;
141    }
142  }
143
144  void run()
145  {
146    transaction_type trans;
147    sc_core::sc_time t;
148
149    while (initTransaction(trans)) {
150      // Create transaction and initialise t
151      t = sc_core::SC_ZERO_TIME;
152
153      logStartTransation(trans);
154
155      ///////////////////////////////////////////////////////////
156      // DMI handling:
157      // We do *not* use the DMI hint to check if it makes sense to ask for
158      // DMI pointers. So the pattern is:
159      // - if the address is not covered by a DMI region try to acquire DMI
160      //   pointers
161      // - if we have a DMI pointer, do the DMI "transaction"
162      // - otherwise fall back to a normal transaction
163      ///////////////////////////////////////////////////////////
164
165      std::pair<dmi_type, bool>& dmi_data = getDMIData(trans);
166
167      // Check if we need to acquire a DMI pointer
168      if((trans.get_address() < dmi_data.first.get_start_address()) ||
169         (trans.get_address() > dmi_data.first.get_end_address()) )
170      {
171          sc_dt::uint64 address = trans.get_address(); //save original address
172          dmi_data.second =
173            socket->get_direct_mem_ptr(trans,
174                                       dmi_data.first);
175          trans.set_address(address);
176      }
177      // Do DMI "transaction" if we have a valid region
178      if (dmi_data.second &&
179          (trans.get_address() >= dmi_data.first.get_start_address()) &&
180          (trans.get_address() <= dmi_data.first.get_end_address()) )
181      {
182          // We can handle the data here. As the logEndTransaction is assuming
183          // something to happen in the data structure, we really need to
184          // do this:
185          trans.set_response_status(tlm::TLM_OK_RESPONSE);
186          sc_dt::uint64 tmp = trans.get_address() - dmi_data.first.get_start_address();
187          if (trans.get_command() == tlm::TLM_WRITE_COMMAND)
188          {
189              *(unsigned int*)&dmi_data.first.get_dmi_ptr()[tmp] = mData;
190          }
191          else
192          {
193              mData = *(unsigned int*)&dmi_data.first.get_dmi_ptr()[tmp];
194          }
195
196          // Do the wait immediately. Note that doing the wait here eats almost
197          // all the performance anyway, so we only gain something if we're
198          // using temporal decoupling.
199          if (trans.get_command() == tlm::TLM_WRITE_COMMAND) {
200            wait(dmi_data.first.get_write_latency());
201
202          } else {
203            wait(dmi_data.first.get_read_latency());
204          }
205      }
206      else // we need a full transaction
207      {
208          socket->b_transport(trans, t);
209          wait(t);
210      }
211      logEndTransaction(trans);
212    }
213    wait();
214
215  }
216
217  // Invalidate DMI pointer(s)
218  void invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
219                                 sc_dt::uint64 end_range)
220  {
221    // FIXME: probably faster to always invalidate everything?
222    if (start_range <= mDMIDataReads.first.get_end_address ()&&
223        end_range >= mDMIDataReads.first.get_start_address()) {
224        mDMIDataReads.second = false;
225    }
226    if (start_range <= mDMIDataWrites.first.get_end_address ()&&
227        end_range >= mDMIDataWrites.first.get_start_address()) {
228      mDMIDataWrites.second = false;
229    }
230  }
231
232  // Test for transport_dbg, this one should fail in bus_dmi as we address
233  // a target that doesn't support transport_dbg:
234  // FIXME: use a configurable address
235  void end_of_simulation()
236  {
237    std::cout <<  name() << ", <<SimpleLTInitiator1>>:" << std::endl
238              << std::endl;
239    unsigned char data[32];
240
241    transaction_type trans;
242    trans.set_address(mBaseAddress);
243    trans.set_data_length(32);
244    trans.set_data_ptr(data);
245    trans.set_read();
246
247    unsigned int n = socket->transport_dbg(trans);
248
249    std::cout << "Mem @" << std::hex << mBaseAddress << std::endl;
250    unsigned int j = 0;
251
252    if (n > 0)
253    {
254        // always align endianness, so that we don't get a diff when
255        // printing the raw data
256        int e_start = 0;
257        int e_end = 4;
258        int e_increment = 1;
259        if (!tlm::host_has_little_endianness())
260        {
261            e_start = 3;
262            e_end = -1;
263            e_increment = -1;
264        }
265
266        for (unsigned int i=0; i<n; i+=4)
267        {
268            for (int k=e_start; k!=e_end; k+=e_increment)
269            {
270                std::cout << std::setw(2) << std::setfill('0')
271                          << (int)data[i+k];
272                j++;
273                if (j==16) {
274                    j=0;
275                    std::cout << std::endl;
276                } else {
277                    std::cout << " ";
278                }
279            }
280        }
281    }
282    else
283    {
284        std::cout << "OK: debug transaction didn't give data." << std::endl;
285    }
286    std::cout << std::dec << std::endl;
287  }
288private:
289  std::pair<dmi_type, bool> mDMIDataReads;
290  std::pair<dmi_type, bool> mDMIDataWrites;
291
292  sc_core::sc_event mEndEvent;
293  unsigned int mNrOfTransactions;
294  unsigned int mBaseAddress;
295  unsigned int mTransactionCount;
296  unsigned int mData;
297};
298
299#endif
300