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_event.cpp --
23
24  Original Author: Martin Janssen, Synopsys, Inc., 2001-05-21
25
26 CHANGE LOG APPEARS AT THE END OF THE FILE
27 *****************************************************************************/
28
29#include <stdlib.h>
30#include <string.h>
31
32#include "sysc/kernel/sc_event.h"
33#include "sysc/kernel/sc_kernel_ids.h"
34#include "sysc/kernel/sc_phase_callback_registry.h"
35#include "sysc/kernel/sc_process.h"
36#include "sysc/kernel/sc_process_handle.h"
37#include "sysc/kernel/sc_simcontext_int.h"
38#include "sysc/kernel/sc_object_manager.h"
39#include "sysc/utils/sc_utils_ids.h"
40
41namespace sc_core {
42
43// ----------------------------------------------------------------------------
44//  CLASS : sc_event
45//
46//  The event class.
47// ----------------------------------------------------------------------------
48
49const char*
50sc_event::basename() const
51{
52    const char* p = strrchr( m_name.c_str(), SC_HIERARCHY_CHAR );
53    return p ? (p + 1) : m_name.c_str();
54}
55
56void
57sc_event::cancel()
58{
59    // cancel a delta or timed notification
60    switch( m_notify_type ) {
61    case DELTA: {
62        // remove this event from the delta events set
63        m_simc->remove_delta_event( this );
64        m_notify_type = NONE;
65        break;
66    }
67    case TIMED: {
68        // remove this event from the timed events set
69        sc_assert( m_timed != 0 );
70        m_timed->m_event = 0;
71        m_timed = 0;
72        m_notify_type = NONE;
73        break;
74    }
75    default:
76        ;
77    }
78}
79
80
81void
82sc_event::notify()
83{
84    // immediate notification
85    if(
86        // coming from sc_prim_channel::update
87        m_simc->update_phase()
88#if SC_HAS_PHASE_CALLBACKS_
89        // coming from phase callbacks
90        || m_simc->notify_phase()
91#endif
92      )
93    {
94        SC_REPORT_ERROR( SC_ID_IMMEDIATE_NOTIFICATION_, "" );
95        return;
96    }
97    cancel();
98    trigger();
99}
100
101void
102sc_event::notify( const sc_time& t )
103{
104    if( m_notify_type == DELTA ) {
105        return;
106    }
107    if( t == SC_ZERO_TIME ) {
108#       if SC_HAS_PHASE_CALLBACKS_
109            if( SC_UNLIKELY_( m_simc->get_status()
110                              & (SC_END_OF_UPDATE|SC_BEFORE_TIMESTEP) ) )
111            {
112                std::stringstream msg;
113                msg << m_simc->get_status()
114                    << ":\n\t delta notification of `"
115                    << name() << "' ignored";
116                SC_REPORT_WARNING( SC_ID_PHASE_CALLBACK_FORBIDDEN_
117                                 , msg.str().c_str() );
118                return;
119            }
120#       endif
121        if( m_notify_type == TIMED ) {
122            // remove this event from the timed events set
123            sc_assert( m_timed != 0 );
124            m_timed->m_event = 0;
125            m_timed = 0;
126        }
127        // add this event to the delta events set
128        m_delta_event_index = m_simc->add_delta_event( this );
129        m_notify_type = DELTA;
130        return;
131    }
132#   if SC_HAS_PHASE_CALLBACKS_
133        if( SC_UNLIKELY_( m_simc->get_status()
134                        & (SC_END_OF_UPDATE|SC_BEFORE_TIMESTEP) ) )
135        {
136            std::stringstream msg;
137            msg << m_simc->get_status()
138                << ":\n\t timed notification of `"
139                << name() << "' ignored";
140            SC_REPORT_WARNING( SC_ID_PHASE_CALLBACK_FORBIDDEN_
141                             , msg.str().c_str() );
142            return;
143        }
144#   endif
145    if( m_notify_type == TIMED ) {
146        sc_assert( m_timed != 0 );
147        if( m_timed->m_notify_time <= m_simc->time_stamp() + t ) {
148            return;
149        }
150        // remove this event from the timed events set
151        m_timed->m_event = 0;
152        m_timed = 0;
153    }
154    // add this event to the timed events set
155    sc_event_timed* et = new sc_event_timed( this, m_simc->time_stamp() + t );
156    m_simc->add_timed_event( et );
157    m_timed = et;
158    m_notify_type = TIMED;
159}
160
161static void sc_warn_notify_delayed()
162{
163    static bool warn_notify_delayed=true;
164    if ( warn_notify_delayed )
165    {
166        warn_notify_delayed = false;
167        SC_REPORT_INFO(SC_ID_IEEE_1666_DEPRECATION_,
168      "notify_delayed(...) is deprecated, use notify(sc_time) instead" );
169    }
170}
171
172void
173sc_event::notify_delayed()
174{
175    sc_warn_notify_delayed();
176    if( m_notify_type != NONE ) {
177        SC_REPORT_ERROR( SC_ID_NOTIFY_DELAYED_, 0 );
178    }
179    // add this event to the delta events set
180    m_delta_event_index = m_simc->add_delta_event( this );
181    m_notify_type = DELTA;
182}
183
184void
185sc_event::notify_delayed( const sc_time& t )
186{
187    sc_warn_notify_delayed();
188    if( m_notify_type != NONE ) {
189        SC_REPORT_ERROR( SC_ID_NOTIFY_DELAYED_, 0 );
190    }
191    if( t == SC_ZERO_TIME ) {
192        // add this event to the delta events set
193        m_delta_event_index = m_simc->add_delta_event( this );
194        m_notify_type = DELTA;
195    } else {
196        // add this event to the timed events set
197        sc_event_timed* et = new sc_event_timed( this,
198                                                 m_simc->time_stamp() + t );
199        m_simc->add_timed_event( et );
200        m_timed = et;
201        m_notify_type = TIMED;
202    }
203}
204
205// +----------------------------------------------------------------------------
206// |"sc_event::register_event"
207// |
208// | This method sets the name of this object instance and optionally adds
209// | it to the object manager's hierarchy. The object instance will be
210// | inserted into the object manager's hierarchy if one of the following is
211// | true:
212// |   (a) the leaf name is non-null and does not start with
213// |       SC_KERNEL_EVENT_PREFIX.
214// |   (b) the event is being created before the start of simulation.
215// |
216// | Arguments:
217// |     leaf_name = leaf name of the object or NULL.
218// +----------------------------------------------------------------------------
219void sc_event::register_event( const char* leaf_name )
220{
221    sc_object_manager* object_manager = m_simc->get_object_manager();
222    m_parent_p = m_simc->active_object();
223
224    // No name provided, if we are not executing then create a name:
225
226    if( !leaf_name || !leaf_name[0] )
227    {
228	if ( sc_is_running( m_simc ) ) return;
229        leaf_name = sc_gen_unique_name("event");
230    }
231
232    // Create a hierarchichal name and place it into the object manager if
233    // its not a kernel event:
234
235    object_manager->create_name( leaf_name ).swap( m_name );
236
237    if ( strncmp( leaf_name, SC_KERNEL_EVENT_PREFIX,
238                  strlen(SC_KERNEL_EVENT_PREFIX) ) )
239    {
240	object_manager->insert_event(m_name, this);
241	if ( m_parent_p )
242	    m_parent_p->add_child_event( this );
243	else
244	    m_simc->add_child_event( this );
245    }
246}
247
248void
249sc_event::reset()
250{
251    m_notify_type = NONE;
252    m_delta_event_index = -1;
253    m_timed = 0;
254    // clear the dynamic sensitive methods
255    m_methods_dynamic.resize(0);
256    // clear the dynamic sensitive threads
257    m_threads_dynamic.resize(0);
258}
259
260// +----------------------------------------------------------------------------
261// |"sc_event::sc_event(name)"
262// |
263// | This is the object instance constructor for named sc_event instances.
264// | If the name is non-null or the this is during elaboration add the
265// | event to the object hierarchy.
266// |
267// | Arguments:
268// |     name = name of the event.
269// +----------------------------------------------------------------------------
270sc_event::sc_event( const char* name ) :
271    m_name(),
272    m_parent_p(NULL),
273    m_simc( sc_get_curr_simcontext() ),
274    m_notify_type( NONE ),
275    m_delta_event_index( -1 ),
276    m_timed( 0 ),
277    m_methods_static(),
278    m_methods_dynamic(),
279    m_threads_static(),
280    m_threads_dynamic()
281{
282    // Skip simulator's internally defined events.
283
284    register_event( name );
285}
286
287// +----------------------------------------------------------------------------
288// |"sc_event::sc_event(name)"
289// |
290// | This is the object instance constructor for non-named sc_event instances.
291// | If this is during elaboration add create a name and add it to the object
292// | hierarchy.
293// +----------------------------------------------------------------------------
294sc_event::sc_event() :
295    m_name(),
296    m_parent_p(NULL),
297    m_simc( sc_get_curr_simcontext() ),
298    m_notify_type( NONE ),
299    m_delta_event_index( -1 ),
300    m_timed( 0 ),
301    m_methods_static(),
302    m_methods_dynamic(),
303    m_threads_static(),
304    m_threads_dynamic()
305{
306
307    register_event( NULL );
308}
309
310// +----------------------------------------------------------------------------
311// |"sc_event::~sc_event"
312// |
313// | This is the object instance destructor for this class. It cancels any
314// | outstanding waits and removes the event from the object manager's
315// | instance table if it has a name.
316// +----------------------------------------------------------------------------
317sc_event::~sc_event()
318{
319    cancel();
320    if ( m_name.length() != 0 )
321    {
322	sc_object_manager* object_manager_p = m_simc->get_object_manager();
323	object_manager_p->remove_event( m_name );
324    }
325}
326
327// +----------------------------------------------------------------------------
328// |"sc_event::trigger"
329// |
330// | This method "triggers" this object instance. This consists of scheduling
331// | for execution all the processes that are schedulable and waiting on this
332// | event.
333// +----------------------------------------------------------------------------
334void
335sc_event::trigger()
336{
337    int       last_i; // index of last element in vector now accessing.
338    int       size;   // size of vector now accessing.
339
340
341    // trigger the static sensitive methods
342
343    if( ( size = m_methods_static.size() ) != 0 )
344    {
345        sc_method_handle* l_methods_static = &m_methods_static[0];
346        int i = size - 1;
347        do {
348            sc_method_handle method_h = l_methods_static[i];
349            method_h->trigger_static();
350        } while( -- i >= 0 );
351    }
352
353    // trigger the dynamic sensitive methods
354
355
356    if( ( size = m_methods_dynamic.size() ) != 0 )
357    {
358	last_i = size - 1;
359	sc_method_handle* l_methods_dynamic = &m_methods_dynamic[0];
360	for ( int i = 0; i <= last_i; i++ )
361	{
362	    sc_method_handle method_h = l_methods_dynamic[i];
363	    if ( method_h->trigger_dynamic( this ) )
364	    {
365		l_methods_dynamic[i] = l_methods_dynamic[last_i];
366		last_i--;
367		i--;
368	    }
369	}
370        m_methods_dynamic.resize(last_i+1);
371    }
372
373
374    // trigger the static sensitive threads
375
376    if( ( size = m_threads_static.size() ) != 0 )
377    {
378        sc_thread_handle* l_threads_static = &m_threads_static[0];
379        int i = size - 1;
380        do {
381            sc_thread_handle thread_h = l_threads_static[i];
382            thread_h->trigger_static();
383        } while( -- i >= 0 );
384    }
385
386    // trigger the dynamic sensitive threads
387
388    if( ( size = m_threads_dynamic.size() ) != 0 )
389    {
390	last_i = size - 1;
391	sc_thread_handle* l_threads_dynamic = &m_threads_dynamic[0];
392	for ( int i = 0; i <= last_i; i++ )
393	{
394	    sc_thread_handle thread_h = l_threads_dynamic[i];
395	    if ( thread_h->trigger_dynamic( this ) )
396	    {
397		l_threads_dynamic[i] = l_threads_dynamic[last_i];
398		i--;
399		last_i--;
400	    }
401	}
402        m_threads_dynamic.resize(last_i+1);
403    }
404
405    m_notify_type = NONE;
406    m_delta_event_index = -1;
407    m_timed = 0;
408}
409
410
411bool
412sc_event::remove_static( sc_method_handle method_h_ ) const
413{
414    int size;
415    if ( ( size = m_methods_static.size() ) != 0 ) {
416      sc_method_handle* l_methods_static = &m_methods_static[0];
417      for( int i = size - 1; i >= 0; -- i ) {
418          if( l_methods_static[i] == method_h_ ) {
419              l_methods_static[i] = l_methods_static[size - 1];
420              m_methods_static.resize(size-1);
421              return true;
422          }
423      }
424    }
425    return false;
426}
427
428bool
429sc_event::remove_static( sc_thread_handle thread_h_ ) const
430{
431    int size;
432    if ( ( size = m_threads_static.size() ) != 0 ) {
433      sc_thread_handle* l_threads_static = &m_threads_static[0];
434      for( int i = size - 1; i >= 0; -- i ) {
435          if( l_threads_static[i] == thread_h_ ) {
436              l_threads_static[i] = l_threads_static[size - 1];
437              m_threads_static.resize(size-1);
438              return true;
439          }
440      }
441    }
442    return false;
443}
444
445bool
446sc_event::remove_dynamic( sc_method_handle method_h_ ) const
447{
448    int size;
449    if ( ( size = m_methods_dynamic.size() ) != 0 ) {
450      sc_method_handle* l_methods_dynamic = &m_methods_dynamic[0];
451      for( int i = size - 1; i >= 0; -- i ) {
452          if( l_methods_dynamic[i] == method_h_ ) {
453              l_methods_dynamic[i] = l_methods_dynamic[size - 1];
454              m_methods_dynamic.resize(size-1);
455              return true;
456          }
457      }
458    }
459    return false;
460}
461
462bool
463sc_event::remove_dynamic( sc_thread_handle thread_h_ ) const
464{
465    int size;
466    if ( ( size= m_threads_dynamic.size() ) != 0 ) {
467      sc_thread_handle* l_threads_dynamic = &m_threads_dynamic[0];
468      for( int i = size - 1; i >= 0; -- i ) {
469          if( l_threads_dynamic[i] == thread_h_ ) {
470              l_threads_dynamic[i] = l_threads_dynamic[size - 1];
471              m_threads_dynamic.resize(size-1);
472              return true;
473          }
474      }
475    }
476    return false;
477}
478
479
480// ----------------------------------------------------------------------------
481//  CLASS : sc_event_timed
482//
483//  Class for storing the time to notify a timed event.
484// ----------------------------------------------------------------------------
485
486// dedicated memory management; not MT-Safe
487
488union sc_event_timed_u
489{
490    sc_event_timed_u* next;
491    char              dummy[sizeof( sc_event_timed )];
492};
493
494static
495sc_event_timed_u* free_list = 0;
496
497void*
498sc_event_timed::allocate()
499{
500    const int ALLOC_SIZE = 64;
501
502    if( free_list == 0 ) {
503        free_list = (sc_event_timed_u*) malloc( ALLOC_SIZE *
504                                                sizeof( sc_event_timed ) );
505        int i = 0;
506        for( ; i < ALLOC_SIZE - 1; ++ i ) {
507            free_list[i].next = &free_list[i + 1];
508        }
509        free_list[i].next = 0;
510    }
511
512    sc_event_timed_u* q = free_list;
513    free_list = free_list->next;
514    return q;
515}
516
517void
518sc_event_timed::deallocate( void* p )
519{
520    if( p != 0 ) {
521        sc_event_timed_u* q = RCAST<sc_event_timed_u*>( p );
522        q->next = free_list;
523        free_list = q;
524    }
525}
526
527
528// ----------------------------------------------------------------------------
529//  CLASS : sc_event_list
530//
531//  Base class for lists of events.
532// ----------------------------------------------------------------------------
533
534void
535sc_event_list::push_back( const sc_event& e )
536{
537    // make sure e is not already in the list
538    if ( m_events.size() != 0 ) {
539      const sc_event** l_events = &m_events[0];
540      for( int i = m_events.size() - 1; i >= 0; -- i ) {
541          if( &e == l_events[i] ) {
542              // event already in the list; ignore
543              return;
544          }
545      }
546    }
547    m_events.push_back( &e );
548}
549
550void
551sc_event_list::push_back( const sc_event_list& el )
552{
553    m_events.reserve( size() + el.size() );
554    for ( int i = el.m_events.size() - 1; i >= 0; --i )
555    {
556        push_back( *el.m_events[i] );
557    }
558    el.auto_delete();
559}
560
561void
562sc_event_list::add_dynamic( sc_method_handle method_h ) const
563{
564    m_busy++;
565    if ( m_events.size() != 0 ) {
566      const sc_event* const * l_events = &m_events[0];
567      for( int i = m_events.size() - 1; i >= 0; -- i ) {
568          l_events[i]->add_dynamic( method_h );
569      }
570  }
571}
572
573void
574sc_event_list::add_dynamic( sc_thread_handle thread_h ) const
575{
576    m_busy++;
577    if ( m_events.size() != 0 ) {
578      const sc_event* const* l_events = &m_events[0];
579      for( int i = m_events.size() - 1; i >= 0; -- i ) {
580          l_events[i]->add_dynamic( thread_h );
581      }
582  }
583}
584
585void
586sc_event_list::remove_dynamic( sc_method_handle method_h,
587                               const sc_event* e_not ) const
588{
589    if ( m_events.size() != 0 ) {
590      const sc_event* const* l_events = &m_events[0];
591      for( int i = m_events.size() - 1; i >= 0; -- i ) {
592          const sc_event* e = l_events[i];
593          if( e != e_not ) {
594              e->remove_dynamic( method_h );
595          }
596      }
597  }
598}
599
600void
601sc_event_list::remove_dynamic( sc_thread_handle thread_h,
602                               const sc_event* e_not ) const
603{
604    if ( m_events.size() != 0 ) {
605      const sc_event* const* l_events = &m_events[0];
606      for( int i = m_events.size() - 1; i >= 0; -- i ) {
607          const sc_event* e = l_events[i];
608          if( e != e_not ) {
609              e->remove_dynamic( thread_h );
610          }
611      }
612  }
613}
614
615void
616sc_event_list::report_premature_destruction() const
617{
618    // TDB: reliably detect premature destruction
619    //
620    // If an event list is used as a member of a module,
621    // its lifetime may (correctly) end, although there
622    // are processes currently waiting for it.
623    //
624    // Detecting (and ignoring) this corner-case is quite
625    // difficult for similar reasons to the sc_is_running()
626    // return value during the destruction of the module
627    // hierarchy.
628    //
629    // Ignoring the lifetime checks for now, if no process
630    // is currently running (which is only part of the story):
631
632    if( sc_get_current_process_handle().valid() ) {
633        // FIXME: improve error-handling
634        sc_assert( false && "sc_event_list prematurely destroyed" );
635    }
636
637}
638
639void
640sc_event_list::report_invalid_modification() const
641{
642    // FIXME: improve error-handling
643    sc_assert( false && "sc_event_list modfied while being waited on" );
644}
645
646// ----------------------------------------------------------------------------
647//  Deprecated functional notation for notifying events.
648// ----------------------------------------------------------------------------
649
650static void sc_warn_notify()
651{
652    static bool warn_notify=true;
653    if ( warn_notify )
654    {
655  SC_REPORT_INFO(SC_ID_IEEE_1666_DEPRECATION_,
656      "the notify() function is deprecated use sc_event::notify()" );
657  warn_notify = false;
658    }
659}
660
661void
662notify( sc_event& e )
663{
664    sc_warn_notify();
665    e.notify();
666}
667
668void
669notify( const sc_time& t, sc_event& e )
670{
671    sc_warn_notify();
672    e.notify( t );
673}
674
675void
676notify( double v, sc_time_unit tu, sc_event& e )
677{
678    sc_warn_notify();
679    e.notify( v, tu );
680}
681
682} // namespace sc_core
683
684// $Log: sc_event.cpp,v $
685// Revision 1.17  2011/08/26 20:46:09  acg
686//  Andy Goodrich: moved the modification log to the end of the file to
687//  eliminate source line number skew when check-ins are done.
688//
689// Revision 1.16  2011/08/24 22:05:50  acg
690//  Torsten Maehne: initialization changes to remove warnings.
691//
692// Revision 1.15  2011/03/12 21:07:51  acg
693//  Andy Goodrich: changes to kernel generated event support.
694//
695// Revision 1.14  2011/03/06 15:55:52  acg
696//  Andy Goodrich: changes for named events.
697//
698// Revision 1.13  2011/03/05 01:39:21  acg
699//  Andy Goodrich: changes for named events.
700//
701// Revision 1.12  2011/02/19 08:33:25  acg
702//  Andy Goodrich: remove }'s that should have been removed before.
703//
704// Revision 1.11  2011/02/19 08:30:53  acg
705//  Andy Goodrich: Moved process queueing into trigger_static from
706//  sc_event::notify.
707//
708// Revision 1.10  2011/02/18 20:27:14  acg
709//  Andy Goodrich: Updated Copyrights.
710//
711// Revision 1.9  2011/02/17 19:49:51  acg
712//  Andy Goodrich:
713//    (1) Changed signature of trigger_dynamic() to return a bool again.
714//    (2) Moved process run queue processing into trigger_dynamic().
715//
716// Revision 1.8  2011/02/16 22:37:30  acg
717//  Andy Goodrich: clean up to remove need for ps_disable_pending.
718//
719// Revision 1.7  2011/02/13 21:47:37  acg
720//  Andy Goodrich: update copyright notice.
721//
722// Revision 1.6  2011/02/01 21:02:28  acg
723//  Andy Goodrich: new return code for trigger_dynamic() calls.
724//
725// Revision 1.5  2011/01/18 20:10:44  acg
726//  Andy Goodrich: changes for IEEE1666_2011 semantics.
727//
728// Revision 1.4  2011/01/06 18:04:05  acg
729//  Andy Goodrich: added code to leave disabled processes on the dynamic
730//  method and thread queues.
731//
732// Revision 1.3  2008/05/22 17:06:25  acg
733//  Andy Goodrich: updated copyright notice to include 2008.
734//
735// Revision 1.2  2007/01/17 22:44:30  acg
736//  Andy Goodrich: fix for Microsoft compiler.
737//
738// Revision 1.7  2006/04/11 23:13:20  acg
739//   Andy Goodrich: Changes for reduced reset support that only includes
740//   sc_cthread, but has preliminary hooks for expanding to method and thread
741//   processes also.
742//
743// Revision 1.6  2006/01/25 00:31:19  acg
744//  Andy Goodrich: Changed over to use a standard message id of
745//  SC_ID_IEEE_1666_DEPRECATION for all deprecation messages.
746//
747// Revision 1.5  2006/01/24 20:59:11  acg
748//  Andy Goodrich: fix up of CVS comments, new version roll.
749//
750// Revision 1.4  2006/01/24 20:48:14  acg
751// Andy Goodrich: added deprecation warnings for notify_delayed(). Added two
752// new implementation-dependent methods, notify_next_delta() & notify_internal()
753// to replace calls to notify_delayed() from within the simulator. These two
754// new methods are simpler than notify_delayed() and should speed up simulations
755//
756// Revision 1.3  2006/01/13 18:44:29  acg
757// Added $Log to record CVS changes into the source.
758
759// Taf!
760