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
22  sc_phase_callback_registry.cpp -- Implementation of phase callback registry
23
24  Original Author: Philipp A. Hartmann, OFFIS, 2013-02-15
25
26  CHANGE LOG AT END OF FILE
27 *****************************************************************************/
28
29#include "sysc/kernel/sc_object.h"
30#include "sysc/kernel/sc_phase_callback_registry.h"
31#include "sysc/kernel/sc_kernel_ids.h"
32#include "sysc/utils/sc_report.h"
33
34#include <algorithm>
35#include <functional>
36
37namespace sc_core {
38
39#if SC_HAS_PHASE_CALLBACKS_
40
41sc_phase_callback_registry::sc_phase_callback_registry( sc_simcontext& simc )
42  : m_simc( &simc )
43#if 0
44  , m_cb_eval_vec()
45#endif
46  , m_cb_update_vec()
47  , m_cb_timestep_vec()
48{}
49
50sc_phase_callback_registry::~sc_phase_callback_registry()
51{}
52
53static const sc_phase_callback_registry::mask_type
54  SC_PHASE_CALLBACK_MASK = SC_STATUS_ANY;
55
56namespace /* anonymous */ {
57
58struct entry_match
59  : std::unary_function< sc_phase_callback_registry::entry, bool >
60{
61    typedef sc_phase_callback_registry::cb_type* ref_type;
62
63    explicit
64    entry_match( ref_type ref )
65      : ref_(ref)
66    {}
67
68    result_type operator()( argument_type e )
69        { return e.target == ref_; }
70private:
71    sc_phase_callback_registry::cb_type * ref_;
72
73}; // entry_match
74
75template< typename T >
76inline void
77erase_remove( std::vector<T>* vec, T const& t )
78{
79    vec->erase( std::remove( vec->begin(), vec->end(), t ) );
80}
81
82}  // namespace anonymous
83
84
85sc_phase_callback_registry::mask_type
86sc_phase_callback_registry::validate_mask( cb_type& cb
87                                         , mask_type m
88                                         , bool warn = false )
89{
90    if( SC_UNLIKELY_(m & ~SC_PHASE_CALLBACK_MASK) )
91    {
92        if( warn )
93        {
94            std::stringstream ss;
95            ss << cb.name() << ": invalid phase callback mask: "
96               << (sc_status)m;
97            SC_REPORT_WARNING( SC_ID_PHASE_CALLBACK_REGISTER_
98                             , ss.str().c_str() );
99        }
100        m &= SC_PHASE_CALLBACK_MASK;
101    }
102
103    mask_type check_mask;
104
105    // elaboration callbacks
106    check_mask = ( SC_ELABORATION
107                 | SC_BEFORE_END_OF_ELABORATION
108                 | SC_END_OF_ELABORATION );
109    if( SC_UNLIKELY_( (m & check_mask ) && m_simc->elaboration_done() ) )
110    {
111        if( warn )
112        {
113            std::stringstream ss;
114            ss << cb.name() << ": elaboration done\n\t "
115               << (sc_status)( m & check_mask ) << " callback(s) ignored";
116            SC_REPORT_WARNING(SC_ID_PHASE_CALLBACK_REGISTER_
117                             , ss.str().c_str() );
118        }
119        m &= ~check_mask;
120    }
121
122    check_mask = (SC_BEFORE_END_OF_ELABORATION | SC_END_OF_ELABORATION);
123    if( SC_UNLIKELY_(m & SC_ELABORATION) )
124    {
125        if( warn )
126        {
127            std::stringstream ss;
128            ss << cb.name() << ": " << SC_ELABORATION
129               << ":\n\t substituted by " << (sc_status)(check_mask);
130            SC_REPORT_WARNING( SC_ID_PHASE_CALLBACK_REGISTER_
131                             , ss.str().c_str() );
132        }
133        m &= ~SC_ELABORATION;
134        m |= check_mask;
135    }
136
137    check_mask = ( SC_END_OF_INITIALIZATION
138#if 0
139                 | SC_END_OF_EVALUATION
140#endif
141                 | SC_END_OF_UPDATE
142                 | SC_BEFORE_TIMESTEP );
143    if( SC_UNLIKELY_(m & SC_RUNNING) )
144    {
145        if( warn )
146        {
147            std::stringstream ss;
148            ss << cb.name() << ": " << SC_RUNNING
149               << ":\n\t substituted by " << (sc_status)(check_mask);
150            SC_REPORT_WARNING( SC_ID_PHASE_CALLBACK_REGISTER_
151                             , ss.str().c_str() );
152        }
153        m &= ~SC_RUNNING;
154        m |= check_mask;
155    }
156    return m;
157}
158
159
160sc_phase_callback_registry::mask_type
161sc_phase_callback_registry::register_callback( cb_type& cb, mask_type m )
162{
163    storage_type::iterator it =
164      find_if( m_cb_vec.begin(), m_cb_vec.end(), entry_match(&cb) );
165
166    m = validate_mask(cb, m, /* warn */ true );
167
168    mask_type diff_mask = m;
169    mask_type new_mask  = m;
170
171    if( it != m_cb_vec.end() ) // update existing entry
172    {
173        // update masks
174        new_mask   =  (*it).mask | m;
175        diff_mask  = ~(*it).mask & m;
176        (*it).mask = new_mask;
177    }
178    else // new entry
179    {
180        if( !m ) // empty, do nothing
181            return SC_UNITIALIZED;
182
183        entry new_entry = { &cb, new_mask };
184        m_cb_vec.push_back( new_entry );
185    }
186
187    // add to callback shortcut sets
188#if 0
189    if( diff_mask & SC_END_OF_EVALUATION )
190        m_cb_eval_vec.push_back( &cb );
191#endif
192    if( diff_mask & SC_END_OF_UPDATE )
193        m_cb_update_vec.push_back( &cb );
194    if( diff_mask & SC_BEFORE_TIMESTEP )
195        m_cb_timestep_vec.push_back( &cb );
196
197    return new_mask;
198}
199
200
201sc_phase_callback_registry::mask_type
202sc_phase_callback_registry::unregister_callback( cb_type& cb, mask_type m )
203{
204    storage_type::iterator it =
205      find_if( m_cb_vec.begin(), m_cb_vec.end(), entry_match(&cb) );
206
207    m = validate_mask(cb, m);
208
209    mask_type diff_mask = m;
210    mask_type new_mask  = m;
211
212    if( it == m_cb_vec.end() ) { // not registered
213        return SC_UNITIALIZED;
214    }
215
216    // update masks
217    new_mask   = (*it).mask & ~m;
218    diff_mask  = (*it).mask & m;
219    (*it).mask = new_mask;
220
221    if( !new_mask )
222        m_cb_vec.erase(it);
223
224    // drop from callback shortcut sets
225#if 0
226    if( diff_mask & SC_END_OF_EVALUATION )
227        erase_remove( &m_cb_eval_vec, &cb );
228#endif
229    if( diff_mask & SC_END_OF_UPDATE )
230        erase_remove( &m_cb_update_vec, &cb );
231    if( diff_mask & SC_BEFORE_TIMESTEP )
232        erase_remove( &m_cb_timestep_vec, &cb );
233
234    return new_mask;
235}
236
237
238// generic implementation (for non-critical callbacks)
239//  - also restores hierarchy around callback object
240void
241sc_phase_callback_registry::do_callback( sc_status s ) const
242{
243    typedef storage_type::const_iterator it_type;
244    storage_type const & vec = m_cb_vec;
245
246    for(it_type it = vec.begin(), end = vec.end(); it != end; ++it) {
247        if( s & it->mask ) {
248            sc_object::hierarchy_scope scope(it->target);
249            it->target->do_simulation_phase_callback();
250        }
251    }
252}
253
254#else // ! SC_HAS_PHASE_CALLBACKS_
255
256sc_phase_callback_registry::sc_phase_callback_registry( sc_simcontext& ){}
257sc_phase_callback_registry::~sc_phase_callback_registry(){}
258
259static inline void
260warn_phase_callbacks( sc_core::sc_object const& obj )
261{
262    static bool warned = false;
263    if (!warned)
264    {
265        std::stringstream ss;
266        ss << obj.name() << ".\n"
267           << "Please recompile SystemC with "
268              "\"SC_ENABLE_SIMULATION_PHASE_CALLBACKS\" defined.";
269        SC_REPORT_WARNING( SC_ID_PHASE_CALLBACKS_UNSUPPORTED_
270                         , ss.str().c_str() );
271    }
272}
273
274sc_phase_callback_registry::mask_type
275sc_phase_callback_registry::register_callback( cb_type& cb, mask_type )
276{
277    warn_phase_callbacks( cb );
278    return SC_UNITIALIZED;
279}
280
281sc_phase_callback_registry::mask_type
282sc_phase_callback_registry::unregister_callback( cb_type& cb, mask_type )
283{
284    warn_phase_callbacks( cb );
285    return SC_UNITIALIZED;
286}
287
288#endif // ! SC_HAS_PHASE_CALLBACKS_
289
290} // namespace sc_core
291
292/*****************************************************************************
293
294  MODIFICATION LOG - modifiers, enter your name, affiliation, date and
295  changes you are making here.
296
297      Name, Affiliation, Date:
298  Description of Modification:
299
300 *****************************************************************************/
301// Taf!
302