1/*
2 * Copyright (c) 2015, University of Kaiserslautern
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 *    this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * 3. Neither the name of the copyright holder nor the names of its
17 *    contributors may be used to endorse or promote products derived from
18 *    this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
24 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 * Authors: Matthias Jung
33 */
34
35#include "sc_target.hh"
36
37using namespace sc_core;
38using namespace std;
39
40Target::Target(sc_core::sc_module_name name,
41    bool debug,
42    unsigned long long int size,
43    unsigned int offset) :
44    socket("socket"),
45    transaction_in_progress(0),
46    response_in_progress(false),
47    next_response_pending(0),
48    end_req_pending(0),
49    m_peq(this, &Target::peq_cb),
50    debug(debug),
51    size(size),
52    offset(offset)
53{
54    /* Register tlm transport functions */
55    socket.register_b_transport(this, &Target::b_transport);
56    socket.register_transport_dbg(this, &Target::transport_dbg);
57    socket.register_nb_transport_fw(this, &Target::nb_transport_fw);
58
59
60    /* allocate storage memory */
61    mem = new unsigned char[size];
62
63    SC_METHOD(execute_transaction_process);
64    sensitive << target_done_event;
65    dont_initialize();
66}
67
68void
69Target::check_address(unsigned long long int addr)
70{
71    if (addr < offset || addr >= offset + size)
72        SC_REPORT_FATAL("Target", "Address out of range. Did you set an "
73                                  "appropriate size and offset?");
74}
75
76void
77Target::b_transport(tlm::tlm_generic_payload& trans, sc_time& delay)
78{
79    /* Execute the read or write commands */
80    execute_transaction(trans);
81}
82
83unsigned int
84Target::transport_dbg(tlm::tlm_generic_payload& trans)
85{
86    check_address(trans.get_address());
87
88    tlm::tlm_command cmd = trans.get_command();
89    sc_dt::uint64    adr = trans.get_address() - offset;
90    unsigned char*   ptr = trans.get_data_ptr();
91    unsigned int     len = trans.get_data_length();
92
93    unsigned char *mem_array_ptr = mem + adr;
94
95    /* Load / Store the access: */
96    if ( cmd == tlm::TLM_READ_COMMAND ) {
97        if (debug) {
98            SC_REPORT_INFO("target", "tlm::TLM_READ_COMMAND");
99        }
100        std::memcpy(ptr, mem_array_ptr, len);
101    } else if ( cmd == tlm::TLM_WRITE_COMMAND ) {
102        if (debug) {
103            SC_REPORT_INFO("target", "tlm::TLM_WRITE_COMMAND");
104        }
105        std::memcpy(mem_array_ptr, ptr, len);
106    }
107
108    return len;
109}
110
111
112/* TLM-2 non-blocking transport method */
113tlm::tlm_sync_enum Target::nb_transport_fw(tlm::tlm_generic_payload& trans,
114                                           tlm::tlm_phase& phase,
115                                           sc_time& delay)
116{
117    /* Queue the transaction until the annotated time has elapsed */
118    m_peq.notify(trans, phase, delay);
119    return tlm::TLM_ACCEPTED;
120}
121
122void
123Target::peq_cb(tlm::tlm_generic_payload& trans,
124               const tlm::tlm_phase& phase)
125{
126    sc_time delay;
127
128    if (phase == tlm::BEGIN_REQ) {
129        if (debug) SC_REPORT_INFO("target", "tlm::BEGIN_REQ");
130
131        /* Increment the transaction reference count */
132        trans.acquire();
133
134        if ( !transaction_in_progress ) {
135            send_end_req(trans);
136        } else {
137            /* Put back-pressure on initiator by deferring END_REQ until
138             * pipeline is clear */
139            end_req_pending = &trans;
140        }
141    } else if (phase == tlm::END_RESP) {
142        /* On receiving END_RESP, the target can release the transaction and
143         * allow other pending transactions to proceed */
144        if (!response_in_progress) {
145            SC_REPORT_FATAL("TLM-2", "Illegal transaction phase END_RESP"
146                            "received by target");
147        }
148
149        transaction_in_progress = 0;
150
151        /* Target itself is now clear to issue the next BEGIN_RESP */
152        response_in_progress = false;
153        if (next_response_pending) {
154            send_response( *next_response_pending );
155            next_response_pending = 0;
156        }
157
158        /* ... and to unblock the initiator by issuing END_REQ */
159        if (end_req_pending) {
160            send_end_req( *end_req_pending );
161            end_req_pending = 0;
162        }
163
164    } else /* tlm::END_REQ or tlm::BEGIN_RESP */ {
165            SC_REPORT_FATAL("TLM-2", "Illegal transaction phase received by"
166                            "target");
167    }
168}
169
170void
171Target::send_end_req(tlm::tlm_generic_payload& trans)
172{
173    tlm::tlm_phase bw_phase;
174    sc_time delay;
175
176    /* Queue the acceptance and the response with the appropriate latency */
177    bw_phase = tlm::END_REQ;
178    delay = sc_time(10.0, SC_NS); // Accept delay
179
180    tlm::tlm_sync_enum status;
181    status = socket->nb_transport_bw(trans, bw_phase, delay);
182
183    /* Ignore return value;
184     * initiator cannot terminate transaction at this point
185     * Queue internal event to mark beginning of response: */
186    delay = delay + sc_time(40.0, SC_NS); // Latency
187    target_done_event.notify(delay);
188
189    assert(transaction_in_progress == 0);
190    transaction_in_progress = &trans;
191}
192
193void
194Target::execute_transaction_process()
195{
196    /* Execute the read or write commands */
197    execute_transaction( *transaction_in_progress );
198
199    /* Target must honor BEGIN_RESP/END_RESP exclusion rule; i.e. must not
200     * send BEGIN_RESP until receiving previous END_RESP or BEGIN_REQ */
201    if (response_in_progress) {
202        /* Target allows only two transactions in-flight */
203        if (next_response_pending) {
204            SC_REPORT_FATAL("TLM-2", "Attempt to have two pending responses"
205                            "in target");
206        }
207        next_response_pending = transaction_in_progress;
208    } else {
209        send_response( *transaction_in_progress );
210    }
211}
212
213void
214Target::execute_transaction(tlm::tlm_generic_payload& trans)
215{
216    check_address(trans.get_address());
217
218    tlm::tlm_command cmd = trans.get_command();
219    sc_dt::uint64    adr = trans.get_address() - offset;
220    unsigned char*   ptr = trans.get_data_ptr();
221    unsigned int     len = trans.get_data_length();
222    unsigned char*   byt = trans.get_byte_enable_ptr();
223    unsigned int     wid = trans.get_streaming_width();
224
225    if ( byt != 0 ) {
226        cout << "Byte Error" << endl;
227        trans.set_response_status( tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE );
228        return;
229    }
230
231    //if ( len > 4 || wid < len ) {
232    //    cout << "Burst Error len=" << len << " wid=" << wid << endl;
233    //    trans.set_response_status( tlm::TLM_BURST_ERROR_RESPONSE );
234    //    return;
235    //}
236
237    unsigned char *mem_array_ptr = mem + adr;
238
239    /* Load / Store the access: */
240    if ( cmd == tlm::TLM_READ_COMMAND ) {
241        if (debug) {
242            SC_REPORT_INFO("target", "tlm::TLM_READ_COMMAND");
243        }
244        std::memcpy(ptr, mem_array_ptr, len);
245    } else if ( cmd == tlm::TLM_WRITE_COMMAND ) {
246        if (debug) {
247            SC_REPORT_INFO("target", "tlm::TLM_WRITE_COMMAND");
248        }
249        std::memcpy(mem_array_ptr, ptr, len);
250    }
251
252    trans.set_response_status( tlm::TLM_OK_RESPONSE );
253}
254
255void
256Target::send_response(tlm::tlm_generic_payload& trans)
257{
258    tlm::tlm_sync_enum status;
259    tlm::tlm_phase bw_phase;
260    sc_time delay;
261
262    response_in_progress = true;
263    bw_phase = tlm::BEGIN_RESP;
264    delay = sc_time(10.0, SC_NS);
265    status = socket->nb_transport_bw( trans, bw_phase, delay );
266
267    if (status == tlm::TLM_UPDATED) {
268        /* The timing annotation must be honored */
269        m_peq.notify(trans, bw_phase, delay);
270    } else if (status == tlm::TLM_COMPLETED) {
271        /* The initiator has terminated the transaction */
272        transaction_in_progress = 0;
273        response_in_progress = false;
274    }
275    trans.release();
276}
277