diff options
Diffstat (limited to 'ext/systemc/src/sysc/kernel/sc_simcontext.cpp')
-rw-r--r-- | ext/systemc/src/sysc/kernel/sc_simcontext.cpp | 2284 |
1 files changed, 2284 insertions, 0 deletions
diff --git a/ext/systemc/src/sysc/kernel/sc_simcontext.cpp b/ext/systemc/src/sysc/kernel/sc_simcontext.cpp new file mode 100644 index 000000000..104f7c984 --- /dev/null +++ b/ext/systemc/src/sysc/kernel/sc_simcontext.cpp @@ -0,0 +1,2284 @@ +/***************************************************************************** + + Licensed to Accellera Systems Initiative Inc. (Accellera) under one or + more contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright ownership. + Accellera licenses this file to you under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with the + License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + *****************************************************************************/ + +/***************************************************************************** + + sc_simcontext.cpp -- Provides a simulation context for use with multiple + simulations. + + Original Author: Stan Y. Liao, Synopsys, Inc. + Martin Janssen, Synopsys, Inc. + + CHANGE LOG AT THE END OF THE FILE + *****************************************************************************/ + +#include <algorithm> + +#define SC_DISABLE_API_VERSION_CHECK // for in-library sc_ver.h inclusion + +#include "sysc/kernel/sc_cor_fiber.h" +#include "sysc/kernel/sc_cor_pthread.h" +#include "sysc/kernel/sc_cor_qt.h" +#include "sysc/kernel/sc_event.h" +#include "sysc/kernel/sc_kernel_ids.h" +#include "sysc/kernel/sc_module.h" +#include "sysc/kernel/sc_module_registry.h" +#include "sysc/kernel/sc_name_gen.h" +#include "sysc/kernel/sc_object_manager.h" +#include "sysc/kernel/sc_cthread_process.h" +#include "sysc/kernel/sc_method_process.h" +#include "sysc/kernel/sc_thread_process.h" +#include "sysc/kernel/sc_process_handle.h" +#include "sysc/kernel/sc_simcontext.h" +#include "sysc/kernel/sc_simcontext_int.h" +#include "sysc/kernel/sc_reset.h" +#include "sysc/kernel/sc_ver.h" +#include "sysc/kernel/sc_boost.h" +#include "sysc/kernel/sc_spawn.h" +#include "sysc/kernel/sc_phase_callback_registry.h" +#include "sysc/communication/sc_port.h" +#include "sysc/communication/sc_export.h" +#include "sysc/communication/sc_prim_channel.h" +#include "sysc/tracing/sc_trace.h" +#include "sysc/utils/sc_mempool.h" +#include "sysc/utils/sc_list.h" +#include "sysc/utils/sc_utils_ids.h" + +// DEBUGGING MACROS: +// +// DEBUG_MSG(NAME,P,MSG) +// MSG = message to print +// NAME = name that must match the process for the message to print, or +// null if the message should be printed unconditionally. +// P = pointer to process message is for, or NULL in which case the +// message will not print. +#if 0 +# define DEBUG_NAME "" +# define DEBUG_MSG(NAME,P,MSG) \ + { \ + if ( P && ( (strlen(NAME)==0) || !strcmp(NAME,P->name())) ) \ + std::cout << "**** " << sc_time_stamp() << " (" \ + << sc_get_current_process_name() << "): " << MSG \ + << " - " << P->name() << std::endl; \ + } +#else +# define DEBUG_MSG(NAME,P,MSG) +#endif + +#if SC_HAS_PHASE_CALLBACKS_ +# define SC_DO_PHASE_CALLBACK_( Kind ) \ + m_phase_cb_registry->Kind() +#else +# define SC_DO_PHASE_CALLBACK_( Kind ) \ + ((void)0) /* do nothing */ +#endif + +#if defined( SC_ENABLE_SIMULATION_PHASE_CALLBACKS_TRACING ) +// use callback based tracing +# define SC_SIMCONTEXT_TRACING_ 0 +#else +// enable tracing via explicit trace_cycle calls from simulator loop +# define SC_SIMCONTEXT_TRACING_ 1 +#endif + +namespace sc_core { + +sc_stop_mode stop_mode = SC_STOP_FINISH_DELTA; + +// ---------------------------------------------------------------------------- +// CLASS : sc_process_table +// +// Container class that keeps track of all method processes, +// (c)thread processes. +// ---------------------------------------------------------------------------- + +class sc_process_table +{ + public: + + sc_process_table(); + ~sc_process_table(); + void push_front( sc_method_handle ); + void push_front( sc_thread_handle ); + sc_method_handle method_q_head(); + sc_method_handle remove( sc_method_handle ); + sc_thread_handle thread_q_head(); + sc_thread_handle remove( sc_thread_handle ); + + + private: + + sc_method_handle m_method_q; // Queue of existing method processes. + sc_thread_handle m_thread_q; // Queue of existing thread processes. +}; + + +// IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII + +sc_process_table::sc_process_table() : m_method_q(0), m_thread_q(0) +{} + +sc_process_table::~sc_process_table() +{ + + sc_method_handle method_next_p; // Next method to delete. + sc_method_handle method_now_p; // Method now deleting. + + for( method_now_p = m_method_q; method_now_p; method_now_p = method_next_p ) + { + method_next_p = method_now_p->next_exist(); + delete method_now_p; + } + + if ( m_thread_q ) + { + ::std::cout << ::std::endl + << "WATCH OUT!! In sc_process_table destructor. " + << "Threads and cthreads are not actually getting deleted here. " + << "Some memory may leak. Look at the comments here in " + << "kernel/sc_simcontext.cpp for more details." + << ::std::endl; + } + + // don't delete threads and cthreads. If a (c)thread + // has died, then it has already been deleted. Only (c)threads created + // before simulation-start are in this table. Due to performance + // reasons, we don't look up the dying thread in the process table + // and remove it from there. simcontext::reset and ~simcontext invoke this + // destructor. At present none of these routines are ever invoked. + // We can delete threads and cthreads here if a dying thread figured out + // it was created before simulation-start and took itself off the + // process_table. + +#if 0 + sc_thread_handle thread_next_p; // Next thread to delete. + sc_thread_handle thread_now_p; // Thread now deleting. + + for( thread_now_p=m_thread_q; thread_now_p; thread_now_p=thread_next_p ) + { + thread_next_p = thread_now_p->next_exist(); + delete thread_now_p; + } +#endif // 0 +} + +inline +sc_method_handle +sc_process_table::method_q_head() +{ + return m_method_q; +} + +inline +void +sc_process_table::push_front( sc_method_handle handle_ ) +{ + handle_->set_next_exist(m_method_q); + m_method_q = handle_; +} + +inline +void +sc_process_table::push_front( sc_thread_handle handle_ ) +{ + handle_->set_next_exist(m_thread_q); + m_thread_q = handle_; +} + +sc_method_handle +sc_process_table::remove( sc_method_handle handle_ ) +{ + sc_method_handle now_p; // Entry now examining. + sc_method_handle prior_p; // Entry prior to one now examining. + + prior_p = 0; + for ( now_p = m_method_q; now_p; now_p = now_p->next_exist() ) + { + if ( now_p == handle_ ) + { + if ( prior_p ) + prior_p->set_next_exist( now_p->next_exist() ); + else + m_method_q = now_p->next_exist(); + return handle_; + } + } + return 0; +} + +sc_thread_handle +sc_process_table::remove( sc_thread_handle handle_ ) +{ + sc_thread_handle now_p; // Entry now examining. + sc_thread_handle prior_p; // Entry prior to one now examining. + + prior_p = 0; + for ( now_p = m_thread_q; now_p; now_p = now_p->next_exist() ) + { + if ( now_p == handle_ ) + { + if ( prior_p ) + prior_p->set_next_exist( now_p->next_exist() ); + else + m_thread_q = now_p->next_exist(); + return handle_; + } + } + return 0; +} + +inline +sc_thread_handle +sc_process_table::thread_q_head() +{ + return m_thread_q; +} + +int +sc_notify_time_compare( const void* p1, const void* p2 ) +{ + const sc_event_timed* et1 = static_cast<const sc_event_timed*>( p1 ); + const sc_event_timed* et2 = static_cast<const sc_event_timed*>( p2 ); + + const sc_time& t1 = et1->notify_time(); + const sc_time& t2 = et2->notify_time(); + + if( t1 < t2 ) { + return 1; + } else if( t1 > t2 ) { + return -1; + } else { + return 0; + } +} + + +// +============================================================================ +// | CLASS sc_invoke_method - class to invoke sc_method's to support +// | sc_simcontext::preempt_with(). +// +============================================================================ +SC_MODULE(sc_invoke_method) +{ + SC_CTOR(sc_invoke_method) + { + // remove from object hierarchy + detach(); + } + + virtual ~sc_invoke_method() + { + m_invokers.resize(0); + } + + // Method to call to execute a method's semantics. + + void invoke_method( sc_method_handle method_h ) + { + sc_process_handle invoker_h; // handle for invocation thread to use. + std::vector<sc_process_handle>::size_type invokers_n; // number of invocation threads available. + + m_method = method_h; + + // There is not an invocation thread to use, so allocate one. + + invokers_n = m_invokers.size(); + if ( invokers_n == 0 ) + { + sc_spawn_options options; + options.dont_initialize(); + options.set_stack_size(0x100000); + options.set_sensitivity(&m_dummy); + invoker_h = sc_spawn(sc_bind(&sc_invoke_method::invoker,this), + sc_gen_unique_name("invoker"), &options); + ((sc_process_b*)invoker_h)->detach(); + } + + // There is an invocation thread to use, use the last one on the list. + + else + { + invoker_h = m_invokers[invokers_n-1]; + m_invokers.pop_back(); + } + + // Fire off the invocation thread to invoke the method's semantics, + // When it blocks put it onto the list of invocation threads that + // are available. + + sc_get_curr_simcontext()->preempt_with( (sc_thread_handle)invoker_h ); + DEBUG_MSG( DEBUG_NAME, m_method, "back from preemption" ); + m_invokers.push_back(invoker_h); + } + + // Thread to call method from: + + void invoker() + { + sc_simcontext* csc_p = sc_get_curr_simcontext(); + sc_process_b* me = sc_get_current_process_b(); + + DEBUG_MSG( DEBUG_NAME, me, "invoker initialization" ); + for (;; ) + { + DEBUG_MSG( DEBUG_NAME, m_method, "invoker executing method" ); + csc_p->set_curr_proc( (sc_process_b*)m_method ); + csc_p->get_active_invokers().push_back((sc_thread_handle)me); + m_method->run_process(); + csc_p->set_curr_proc( me ); + csc_p->get_active_invokers().pop_back(); + DEBUG_MSG( DEBUG_NAME, m_method, "back from executing method" ); + wait(); + } + } + + sc_event m_dummy; // dummy event to wait on. + sc_method_handle m_method; // method to be invoked. + std::vector<sc_process_handle> m_invokers; // list of invoking threads. +}; + +// ---------------------------------------------------------------------------- +// CLASS : sc_simcontext +// +// The simulation context. +// ---------------------------------------------------------------------------- + +void +sc_simcontext::init() +{ + + // ALLOCATE VARIOUS MANAGERS AND REGISTRIES: + + m_object_manager = new sc_object_manager; + m_module_registry = new sc_module_registry( *this ); + m_port_registry = new sc_port_registry( *this ); + m_export_registry = new sc_export_registry( *this ); + m_prim_channel_registry = new sc_prim_channel_registry( *this ); + m_phase_cb_registry = new sc_phase_callback_registry( *this ); + m_name_gen = new sc_name_gen; + m_process_table = new sc_process_table; + m_current_writer = 0; + + + // CHECK FOR ENVIRONMENT VARIABLES THAT MODIFY SIMULATOR EXECUTION: + + const char* write_check = std::getenv("SC_SIGNAL_WRITE_CHECK"); + m_write_check = ( (write_check==0) || strcmp(write_check,"DISABLE") ) ? + true : false; + + + // FINISH INITIALIZATIONS: + + reset_curr_proc(); + m_next_proc_id = -1; + m_timed_events = new sc_ppq<sc_event_timed*>( 128, sc_notify_time_compare ); + m_something_to_trace = false; + m_runnable = new sc_runnable; + m_collectable = new sc_process_list; + m_time_params = new sc_time_params; + m_curr_time = SC_ZERO_TIME; + m_max_time = SC_ZERO_TIME; + m_change_stamp = 0; + m_delta_count = 0; + m_forced_stop = false; + m_paused = false; + m_ready_to_simulate = false; + m_elaboration_done = false; + m_execution_phase = phase_initialize; + m_error = NULL; + m_cor_pkg = 0; + m_method_invoker_p = NULL; + m_cor = 0; + m_in_simulator_control = false; + m_start_of_simulation_called = false; + m_end_of_simulation_called = false; + m_simulation_status = SC_ELABORATION; +} + +void +sc_simcontext::clean() +{ + delete m_object_manager; + delete m_module_registry; + delete m_port_registry; + delete m_export_registry; + delete m_prim_channel_registry; + delete m_phase_cb_registry; + delete m_name_gen; + delete m_process_table; + m_child_objects.resize(0); + m_delta_events.resize(0); + delete m_timed_events; + for( int i = m_trace_files.size() - 1; i >= 0; -- i ) { + delete m_trace_files[i]; + } + m_trace_files.resize(0); + delete m_runnable; + delete m_collectable; + delete m_time_params; + delete m_cor_pkg; + delete m_error; +} + + +sc_simcontext::sc_simcontext() : + m_object_manager(0), m_module_registry(0), m_port_registry(0), + m_export_registry(0), m_prim_channel_registry(0), + m_phase_cb_registry(0), m_name_gen(0), + m_process_table(0), m_curr_proc_info(), m_current_writer(0), + m_write_check(false), m_next_proc_id(-1), m_child_events(), + m_child_objects(), m_delta_events(), m_timed_events(0), m_trace_files(), + m_something_to_trace(false), m_runnable(0), m_collectable(0), + m_time_params(), m_curr_time(SC_ZERO_TIME), m_max_time(SC_ZERO_TIME), + m_change_stamp(0), m_delta_count(0), m_forced_stop(false), m_paused(false), + m_ready_to_simulate(false), m_elaboration_done(false), + m_execution_phase(phase_initialize), m_error(0), + m_in_simulator_control(false), m_end_of_simulation_called(false), + m_simulation_status(SC_ELABORATION), m_start_of_simulation_called(false), + m_cor_pkg(0), m_cor(0) +{ + init(); +} + +sc_simcontext::~sc_simcontext() +{ + clean(); +} + +// +---------------------------------------------------------------------------- +// |"sc_simcontext::active_object" +// | +// | This method returns the currently active object with respect to +// | additions to the hierarchy. It will be the top of the object hierarchy +// | stack if it is non-empty, or it will be the active process, or NULL +// | if there is no active process. +// +---------------------------------------------------------------------------- +sc_object* +sc_simcontext::active_object() +{ + sc_object* result_p; // pointer to return. + + result_p = m_object_manager->hierarchy_curr(); + if ( !result_p ) + result_p = (sc_object*)get_curr_proc_info()->process_handle; + return result_p; +} + +// +---------------------------------------------------------------------------- +// |"sc_simcontext::crunch" +// | +// | This method implements the simulator's execution of processes. It performs +// | one or more "delta" cycles. Each delta cycle consists of an evaluation, +// | an update phase, and a notification phase. During the evaluation phase any +// | processes that are ready to run are executed. After all the processes have +// | been executed the update phase is entered. During the update phase the +// | values of any signals that have changed are updated. After the updates +// | have been performed the notification phase is entered. During that phase +// | any notifications that need to occur because of of signal values changes +// | are performed. This will result in the queueing of processes for execution +// | that are sensitive to those notifications. At that point a delta cycle +// | is complete, and the process is started again unless 'once' is true. +// | +// | Arguments: +// | once = true if only one delta cycle is to be performed. +// +---------------------------------------------------------------------------- +inline void +sc_simcontext::crunch( bool once ) +{ +#ifdef DEBUG_SYSTEMC + int num_deltas = 0; // number of delta cycles +#endif + + while ( true ) + { + + // EVALUATE PHASE + + m_execution_phase = phase_evaluate; + bool empty_eval_phase = true; + while( true ) + { + + // execute method processes + + m_runnable->toggle_methods(); + sc_method_handle method_h = pop_runnable_method(); + while( method_h != 0 ) { + empty_eval_phase = false; + if ( !method_h->run_process() ) + { + goto out; + } + method_h = pop_runnable_method(); + } + + // execute (c)thread processes + + m_runnable->toggle_threads(); + sc_thread_handle thread_h = pop_runnable_thread(); + while( thread_h != 0 ) { + if ( thread_h->m_cor_p != NULL ) break; + thread_h = pop_runnable_thread(); + } + + if( thread_h != 0 ) { + empty_eval_phase = false; + m_cor_pkg->yield( thread_h->m_cor_p ); + } + if( m_error ) { + goto out; + } + + // check for call(s) to sc_stop + if( m_forced_stop ) { + if ( stop_mode == SC_STOP_IMMEDIATE ) goto out; + } + + // no more runnable processes + + if( m_runnable->is_empty() ) { + break; + } + } + + // remove finally dead zombies: + + while( ! m_collectable->empty() ) + { + sc_process_b* del_p = m_collectable->front(); + m_collectable->pop_front(); + del_p->reference_decrement(); + } + + + // UPDATE PHASE + // + // The change stamp must be updated first so that event_occurred() + // will work. + + m_execution_phase = phase_update; + if ( !empty_eval_phase ) + { +// SC_DO_PHASE_CALLBACK_(evaluation_done); + m_change_stamp++; + m_delta_count ++; + } + m_prim_channel_registry->perform_update(); + SC_DO_PHASE_CALLBACK_(update_done); + m_execution_phase = phase_notify; + +#if SC_SIMCONTEXT_TRACING_ + if( m_something_to_trace ) { + trace_cycle( /* delta cycle? */ true ); + } +#endif + + // check for call(s) to sc_stop + if( m_forced_stop ) { + break; + } + +#ifdef DEBUG_SYSTEMC + // check for possible infinite loops + if( ++ num_deltas > SC_MAX_NUM_DELTA_CYCLES ) { + ::std::cerr << "SystemC warning: " + << "the number of delta cycles exceeds the limit of " + << SC_MAX_NUM_DELTA_CYCLES + << ", defined in sc_constants.h.\n" + << "This is a possible sign of an infinite loop.\n" + << "Increase the limit if this warning is invalid.\n"; + break; + } +#endif + + // NOTIFICATION PHASE: + // + // Process delta notifications which will queue processes for + // subsequent execution. + + int size = m_delta_events.size(); + if ( size != 0 ) + { + sc_event** l_events = &m_delta_events[0]; + int i = size - 1; + do { + l_events[i]->trigger(); + } while( -- i >= 0 ); + m_delta_events.resize(0); + } + + if( m_runnable->is_empty() ) { + // no more runnable processes + break; + } + + // if sc_pause() was called we are done. + + if ( m_paused ) break; + + // IF ONLY DOING ONE CYCLE, WE ARE DONE. OTHERWISE EXECUTE NEW CALLBACKS + + if ( once ) break; + } + + // When this point is reached the processing of delta cycles is complete, + // if the completion was because of an error throw the exception specified + // by '*m_error'. +out: + this->reset_curr_proc(); + if( m_error ) throw *m_error; // re-throw propagated error +} + +inline +void +sc_simcontext::cycle( const sc_time& t) +{ + sc_time next_event_time; + + m_in_simulator_control = true; + crunch(); + SC_DO_PHASE_CALLBACK_(before_timestep); +#if SC_SIMCONTEXT_TRACING_ + if( m_something_to_trace ) { + trace_cycle( /* delta cycle? */ false ); + } +#endif + m_curr_time += t; + if ( next_time(next_event_time) && next_event_time <= m_curr_time) { + SC_REPORT_WARNING(SC_ID_CYCLE_MISSES_EVENTS_, ""); + } + m_in_simulator_control = false; + SC_DO_PHASE_CALLBACK_(simulation_paused); +} + +void +sc_simcontext::elaborate() +{ + if( m_elaboration_done || sim_status() != SC_SIM_OK ) { + return; + } + + // Instantiate the method invocation module + // (not added to public object hierarchy) + + m_method_invoker_p = + new sc_invoke_method("$$$$kernel_module$$$$_invoke_method" ); + + m_simulation_status = SC_BEFORE_END_OF_ELABORATION; + for( int cd = 0; cd != 4; /* empty */ ) + { + cd = m_port_registry->construction_done(); + cd += m_export_registry->construction_done(); + cd += m_prim_channel_registry->construction_done(); + cd += m_module_registry->construction_done(); + + // check for call(s) to sc_stop + if( m_forced_stop ) { + do_sc_stop_action(); + return; + } + + } + SC_DO_PHASE_CALLBACK_(construction_done); + + // SIGNAL THAT ELABORATION IS DONE + // + // We set the switch before the calls in case someone creates a process + // in an end_of_elaboration callback. We need the information to flag + // the process as being dynamic. + + m_elaboration_done = true; + m_simulation_status = SC_END_OF_ELABORATION; + + m_port_registry->elaboration_done(); + m_export_registry->elaboration_done(); + m_prim_channel_registry->elaboration_done(); + m_module_registry->elaboration_done(); + SC_DO_PHASE_CALLBACK_(elaboration_done); + sc_reset::reconcile_resets(); + + // check for call(s) to sc_stop + if( m_forced_stop ) { + do_sc_stop_action(); + return; + } +} + +void +sc_simcontext::prepare_to_simulate() +{ + sc_method_handle method_p; // Pointer to method process accessing. + sc_thread_handle thread_p; // Pointer to thread process accessing. + + if( m_ready_to_simulate || sim_status() != SC_SIM_OK ) { + return; + } + + // instantiate the coroutine package + m_cor_pkg = new sc_cor_pkg_t( this ); + m_cor = m_cor_pkg->get_main(); + + // NOTIFY ALL OBJECTS THAT SIMULATION IS ABOUT TO START: + + m_simulation_status = SC_START_OF_SIMULATION; + m_port_registry->start_simulation(); + m_export_registry->start_simulation(); + m_prim_channel_registry->start_simulation(); + m_module_registry->start_simulation(); + SC_DO_PHASE_CALLBACK_(start_simulation); + m_start_of_simulation_called = true; + + // CHECK FOR CALL(S) TO sc_stop + + if( m_forced_stop ) { + do_sc_stop_action(); + return; + } + + // PREPARE ALL (C)THREAD PROCESSES FOR SIMULATION: + + for ( thread_p = m_process_table->thread_q_head(); + thread_p; thread_p = thread_p->next_exist() ) + { + thread_p->prepare_for_simulation(); + } + + m_simulation_status = SC_RUNNING; + m_ready_to_simulate = true; + m_runnable->init(); + + // update phase + + m_execution_phase = phase_update; + m_prim_channel_registry->perform_update(); + m_execution_phase = phase_notify; + + int size; + + // make all method processes runnable + + for ( method_p = m_process_table->method_q_head(); + method_p; method_p = method_p->next_exist() ) + { + if ( ((method_p->m_state & sc_process_b::ps_bit_disabled) != 0) || + method_p->dont_initialize() ) + { + if ( method_p->m_static_events.size() == 0 ) + { + SC_REPORT_WARNING( SC_ID_DISABLE_WILL_ORPHAN_PROCESS_, + method_p->name() ); + } + } + else if ( (method_p->m_state & sc_process_b::ps_bit_suspended) == 0) + { + push_runnable_method_front( method_p ); + } + else + { + method_p->m_state |= sc_process_b::ps_bit_ready_to_run; + } + } + + // make thread processes runnable + // (cthread processes always have the dont_initialize flag set) + + for ( thread_p = m_process_table->thread_q_head(); + thread_p; thread_p = thread_p->next_exist() ) + { + if ( ((thread_p->m_state & sc_process_b::ps_bit_disabled) != 0) || + thread_p->dont_initialize() ) + { + if ( thread_p->m_static_events.size() == 0 ) + { + SC_REPORT_WARNING( SC_ID_DISABLE_WILL_ORPHAN_PROCESS_, + thread_p->name() ); + } + } + else if ( (thread_p->m_state & sc_process_b::ps_bit_suspended) == 0) + { + push_runnable_thread_front( thread_p ); + } + else + { + thread_p->m_state |= sc_process_b::ps_bit_ready_to_run; + } + } + + + // process delta notifications + + if( ( size = m_delta_events.size() ) != 0 ) { + sc_event** l_delta_events = &m_delta_events[0]; + int i = size - 1; + do { + l_delta_events[i]->trigger(); + } while( -- i >= 0 ); + m_delta_events.resize(0); + } + + SC_DO_PHASE_CALLBACK_(initialization_done); +} + +void +sc_simcontext::initial_crunch( bool no_crunch ) +{ + if( no_crunch || m_runnable->is_empty() ) { + return; + } + + // run the delta cycle loop + + crunch(); + if( m_error ) { + return; + } + +#if SC_SIMCONTEXT_TRACING_ + if( m_something_to_trace ) { + trace_cycle( false ); + } +#endif + + // check for call(s) to sc_stop + if( m_forced_stop ) { + do_sc_stop_action(); + } +} + +void +sc_simcontext::initialize( bool no_crunch ) +{ + m_in_simulator_control = true; + elaborate(); + + prepare_to_simulate(); + initial_crunch(no_crunch); + m_in_simulator_control = false; +} + +// +---------------------------------------------------------------------------- +// |"sc_simcontext::simulate" +// | +// | This method runs the simulation for the specified amount of time. +// | +// | Notes: +// | (1) This code always run with an SC_EXIT_ON_STARVATION starvation policy, +// | so the simulation time on return will be the minimum of the +// | simulation on entry plus the duration, and the maximum time of any +// | event present in the simulation. If the simulation policy is +// | SC_RUN_TO_TIME starvation it is implemented by the caller of this +// | method, e.g., sc_start(), by artificially setting the simulation +// | time forward after this method completes. +// | +// | Arguments: +// | duration = amount of time to simulate. +// +---------------------------------------------------------------------------- +void +sc_simcontext::simulate( const sc_time& duration ) +{ + initialize( true ); + + if (sim_status() != SC_SIM_OK) { + return; + } + + sc_time non_overflow_time = sc_max_time() - m_curr_time; + if ( duration > non_overflow_time ) + { + SC_REPORT_ERROR(SC_ID_SIMULATION_TIME_OVERFLOW_, ""); + return; + } + else if ( duration < SC_ZERO_TIME ) + { + SC_REPORT_ERROR(SC_ID_NEGATIVE_SIMULATION_TIME_,""); + } + + m_in_simulator_control = true; + m_paused = false; + + sc_time until_t = m_curr_time + duration; + sc_time t; // current simulaton time. + + // IF DURATION WAS ZERO WE ONLY CRUNCH ONCE: + // + // We duplicate the code so that we don't add the overhead of the + // check to each loop in the do below. + if ( duration == SC_ZERO_TIME ) + { + m_in_simulator_control = true; + crunch( true ); + if( m_error ) { + m_in_simulator_control = false; + return; + } +#if SC_SIMCONTEXT_TRACING_ + if( m_something_to_trace ) + trace_cycle( /* delta cycle? */ false ); +#endif + if( m_forced_stop ) { + do_sc_stop_action(); + return; + } + // return via implicit pause + goto exit_pause; + } + + // NON-ZERO DURATION: EXECUTE UP TO THAT TIME, OR UNTIL EVENT STARVATION: + + do { + + crunch(); + if( m_error ) { + m_in_simulator_control = false; + return; + } +#if SC_SIMCONTEXT_TRACING_ + if( m_something_to_trace ) { + trace_cycle( false ); + } +#endif + // check for call(s) to sc_stop() or sc_pause(). + if( m_forced_stop ) { + do_sc_stop_action(); + return; + } + if( m_paused ) goto exit_pause; // return explicit pause + + t = m_curr_time; + + do { + // See note 1 above: + + if ( !next_time(t) || (t > until_t ) ) goto exit_time; + if ( t > m_curr_time ) + { + SC_DO_PHASE_CALLBACK_(before_timestep); + m_curr_time = t; + m_change_stamp++; + } + + // PROCESS TIMED NOTIFICATIONS AT THE CURRENT TIME + + do { + sc_event_timed* et = m_timed_events->extract_top(); + sc_event* e = et->event(); + delete et; + if( e != 0 ) { + e->trigger(); + } + } while( m_timed_events->size() && + m_timed_events->top()->notify_time() == t ); + + } while( m_runnable->is_empty() ); + } while ( t < until_t ); // hold off on the delta for the until_t time. + +exit_time: // final simulation time update, if needed + if ( t > m_curr_time && t <= until_t ) + { + SC_DO_PHASE_CALLBACK_(before_timestep); + m_curr_time = t; + m_change_stamp++; + } +exit_pause: // call pause callback upon implicit or explicit pause + m_execution_phase = phase_evaluate; + m_in_simulator_control = false; + SC_DO_PHASE_CALLBACK_(simulation_paused); +} + +void +sc_simcontext::do_sc_stop_action() +{ + SC_REPORT_INFO("/OSCI/SystemC","Simulation stopped by user."); + if (m_start_of_simulation_called) { + end(); + m_in_simulator_control = false; + } + m_simulation_status = SC_STOPPED; + SC_DO_PHASE_CALLBACK_(simulation_stopped); +} + +void +sc_simcontext::mark_to_collect_process( sc_process_b* zombie ) +{ + m_collectable->push_back( zombie ); +} + + +//------------------------------------------------------------------------------ +//"sc_simcontext::stop" +// +// This method stops the simulator after some amount of further processing. +// How much processing is done depends upon the value of the global variable +// stop_mode: +// SC_STOP_IMMEDIATE - aborts the execution phase of the current delta +// cycle and performs whatever updates are pending. +// SC_STOP_FINISH_DELTA - finishes the current delta cycle - both execution +// and updates. +// If sc_stop is called outside of the purview of the simulator kernel +// (e.g., directly from sc_main), the end of simulation notifications +// are performed. From within the purview of the simulator kernel, these +// will be performed at a later time. +//------------------------------------------------------------------------------ + +void +sc_simcontext::stop() +{ + static bool stop_warning_issued = false; + if (m_forced_stop) + { + if ( !stop_warning_issued ) + { + stop_warning_issued = true; // This must be before the WARNING!!! + SC_REPORT_WARNING(SC_ID_SIMULATION_STOP_CALLED_TWICE_, ""); + } + return; + } + if ( stop_mode == SC_STOP_IMMEDIATE ) m_runnable->init(); + m_forced_stop = true; + if ( !m_in_simulator_control ) + { + do_sc_stop_action(); + } +} + +void +sc_simcontext::reset() +{ + clean(); + init(); +} + +void +sc_simcontext::end() +{ + m_simulation_status = SC_END_OF_SIMULATION; + m_ready_to_simulate = false; + m_port_registry->simulation_done(); + m_export_registry->simulation_done(); + m_prim_channel_registry->simulation_done(); + m_module_registry->simulation_done(); + SC_DO_PHASE_CALLBACK_(simulation_done); + m_end_of_simulation_called = true; +} + +void +sc_simcontext::hierarchy_push( sc_module* mod ) +{ + m_object_manager->hierarchy_push( mod ); +} + +sc_module* +sc_simcontext::hierarchy_pop() +{ + return static_cast<sc_module*>( m_object_manager->hierarchy_pop() ); +} + +sc_module* +sc_simcontext::hierarchy_curr() const +{ + return static_cast<sc_module*>( m_object_manager->hierarchy_curr() ); +} + +sc_object* +sc_simcontext::first_object() +{ + return m_object_manager->first_object(); +} + +sc_object* +sc_simcontext::next_object() +{ + return m_object_manager->next_object(); +} + +sc_object* +sc_simcontext::find_object( const char* name ) +{ + static bool warn_find_object=true; + if ( warn_find_object ) + { + warn_find_object = false; + SC_REPORT_INFO(SC_ID_IEEE_1666_DEPRECATION_, + "sc_simcontext::find_object() is deprecated,\n" \ + " use sc_find_object()" ); + } + return m_object_manager->find_object( name ); +} + +// to generate unique names for objects in an MT-Safe way + +const char* +sc_simcontext::gen_unique_name( const char* basename_, bool preserve_first ) +{ + return m_name_gen->gen_unique_name( basename_, preserve_first ); +} + + +sc_process_handle +sc_simcontext::create_cthread_process( + const char* name_p, bool free_host, SC_ENTRY_FUNC method_p, + sc_process_host* host_p, const sc_spawn_options* opt_p ) +{ + sc_thread_handle handle = + new sc_cthread_process(name_p, free_host, method_p, host_p, opt_p); + if ( m_ready_to_simulate ) + { + handle->prepare_for_simulation(); + } else { + m_process_table->push_front( handle ); + } + return sc_process_handle(handle); +} + + +sc_process_handle +sc_simcontext::create_method_process( + const char* name_p, bool free_host, SC_ENTRY_FUNC method_p, + sc_process_host* host_p, const sc_spawn_options* opt_p ) +{ + sc_method_handle handle = + new sc_method_process(name_p, free_host, method_p, host_p, opt_p); + if ( m_ready_to_simulate ) { // dynamic process + if ( !handle->dont_initialize() ) + { +#ifdef SC_HAS_PHASE_CALLBACKS_ + if( SC_UNLIKELY_( m_simulation_status + & (SC_END_OF_UPDATE|SC_BEFORE_TIMESTEP) ) ) + { + std::stringstream msg; + msg << m_simulation_status + << ":\n\t immediate method spawning of " + "`" << handle->name() << "' ignored"; + SC_REPORT_WARNING( SC_ID_PHASE_CALLBACK_FORBIDDEN_ + , msg.str().c_str() ); + } + else +#endif // SC_HAS_PHASE_CALLBACKS_ + { + push_runnable_method( handle ); + } + } + else if ( handle->m_static_events.size() == 0 ) + { + SC_REPORT_WARNING( SC_ID_DISABLE_WILL_ORPHAN_PROCESS_, + handle->name() ); + } + + } else { + m_process_table->push_front( handle ); + } + return sc_process_handle(handle); +} + + +sc_process_handle +sc_simcontext::create_thread_process( + const char* name_p, bool free_host, SC_ENTRY_FUNC method_p, + sc_process_host* host_p, const sc_spawn_options* opt_p ) +{ + sc_thread_handle handle = + new sc_thread_process(name_p, free_host, method_p, host_p, opt_p); + if ( m_ready_to_simulate ) { // dynamic process + handle->prepare_for_simulation(); + if ( !handle->dont_initialize() ) + { +#ifdef SC_HAS_PHASE_CALLBACKS_ + if( SC_UNLIKELY_( m_simulation_status + & (SC_END_OF_UPDATE|SC_BEFORE_TIMESTEP) ) ) + { + std::stringstream msg; + msg << m_simulation_status + << ":\n\t immediate thread spawning of " + "`" << handle->name() << "' ignored"; + SC_REPORT_WARNING( SC_ID_PHASE_CALLBACK_FORBIDDEN_ + , msg.str().c_str() ); + } + else +#endif // SC_HAS_PHASE_CALLBACKS_ + { + push_runnable_thread( handle ); + } + } + else if ( handle->m_static_events.size() == 0 ) + { + SC_REPORT_WARNING( SC_ID_DISABLE_WILL_ORPHAN_PROCESS_, + handle->name() ); + } + + } else { + m_process_table->push_front( handle ); + } + return sc_process_handle(handle); +} + +void +sc_simcontext::add_trace_file( sc_trace_file* tf ) +{ + m_trace_files.push_back( tf ); + m_something_to_trace = true; +} + +void +sc_simcontext::remove_trace_file( sc_trace_file* tf ) +{ + m_trace_files.erase( + std::remove( m_trace_files.begin(), m_trace_files.end(), tf ) + ); + m_something_to_trace = ( m_trace_files.size() > 0 ); +} + +sc_cor* +sc_simcontext::next_cor() +{ + if( m_error ) { + return m_cor; + } + + sc_thread_handle thread_h = pop_runnable_thread(); + while( thread_h != 0 ) { + if ( thread_h->m_cor_p != NULL ) break; + thread_h = pop_runnable_thread(); + } + + if( thread_h != 0 ) { + return thread_h->m_cor_p; + } else { + return m_cor; + } +} + +const ::std::vector<sc_object*>& +sc_simcontext::get_child_objects() const +{ + static bool warn_get_child_objects=true; + if ( warn_get_child_objects ) + { + warn_get_child_objects = false; + SC_REPORT_INFO(SC_ID_IEEE_1666_DEPRECATION_, + "sc_simcontext::get_child_objects() is deprecated,\n" \ + " use sc_get_top_level_objects()" ); + } + return m_child_objects; +} + +void +sc_simcontext::add_child_event( sc_event* event_ ) +{ + // no check if object_ is already in the set + m_child_events.push_back( event_ ); +} + +void +sc_simcontext::add_child_object( sc_object* object_ ) +{ + // no check if object_ is already in the set + m_child_objects.push_back( object_ ); +} + +void +sc_simcontext::remove_child_event( sc_event* event_ ) +{ + int size = m_child_events.size(); + for( int i = 0; i < size; ++ i ) { + if( event_ == m_child_events[i] ) { + m_child_events[i] = m_child_events[size - 1]; + m_child_events.resize(size-1); + return; + } + } + // no check if event_ is really in the set +} + +void +sc_simcontext::remove_child_object( sc_object* object_ ) +{ + int size = m_child_objects.size(); + for( int i = 0; i < size; ++ i ) { + if( object_ == m_child_objects[i] ) { + m_child_objects[i] = m_child_objects[size - 1]; + m_child_objects.resize(size-1); + return; + } + } + // no check if object_ is really in the set +} + +sc_dt::uint64 +sc_simcontext::delta_count() const +{ + static bool warn_delta_count=true; + if ( warn_delta_count ) + { + warn_delta_count = false; + SC_REPORT_INFO(SC_ID_IEEE_1666_DEPRECATION_, + "sc_simcontext::delta_count() is deprecated, use sc_delta_count()" ); + } + return m_delta_count; +} + +bool +sc_simcontext::is_running() const +{ + static bool warn_is_running=true; + if ( warn_is_running ) + { + warn_is_running = false; + SC_REPORT_INFO(SC_ID_IEEE_1666_DEPRECATION_, + "sc_simcontext::is_running() is deprecated, use sc_is_running()" ); + } + return m_ready_to_simulate; +} + +// +---------------------------------------------------------------------------- +// |"sc_simcontext::next_time" +// | +// | This method returns the time of the next event. If there are no events +// | it returns false. +// | +// | Arguments: +// | result = where to place time of the next event, if no event is +// | found this value will not be changed. +// | Result is true if an event is found, false if not. +// +---------------------------------------------------------------------------- +bool +sc_simcontext::next_time( sc_time& result ) const +{ + while( m_timed_events->size() ) { + sc_event_timed* et = m_timed_events->top(); + if( et->event() != 0 ) { + result = et->notify_time(); + return true; + } + delete m_timed_events->extract_top(); + } + return false; +} + +void +sc_simcontext::remove_delta_event( sc_event* e ) +{ + int i = e->m_delta_event_index; + int j = m_delta_events.size() - 1; + assert( i >= 0 && i <= j ); + if( i != j ) { + sc_event** l_delta_events = &m_delta_events[0]; + l_delta_events[i] = l_delta_events[j]; + l_delta_events[i]->m_delta_event_index = i; + } + m_delta_events.resize(m_delta_events.size()-1); + e->m_delta_event_index = -1; +} + +// +---------------------------------------------------------------------------- +// |"sc_simcontext::preempt_with" +// | +// | This method executes the supplied method immediately, suspending the +// | caller. After executing the supplied method the caller's execution will +// | be restored. It is used to allow a method to immediately throw an +// | exception, e.g., when the method's kill_process() method was called. +// | There are three cases to consider: +// | (1) The caller is a method, e.g., murder by method. +// | (2) The caller is a thread instance, e.g., murder by thread. +// | (3) The caller is this method instance, e.g., suicide. +// | +// | Arguments: +// | method_h -> method to be executed. +// +---------------------------------------------------------------------------- +void +sc_simcontext::preempt_with( sc_method_handle method_h ) +{ + sc_curr_proc_info caller_info; // process info for caller. + sc_method_handle active_method_h; // active method or null. + sc_thread_handle active_thread_h; // active thread or null. + + // Determine the active process and take the thread to be run off the + // run queue, if its there, since we will be explicitly causing its + // execution. + + active_method_h = DCAST<sc_method_handle>(sc_get_current_process_b()); + active_thread_h = DCAST<sc_thread_handle>(sc_get_current_process_b()); + if ( method_h->next_runnable() != NULL ) + remove_runnable_method( method_h ); + + // CALLER IS THE METHOD TO BE RUN: + // + // Should never get here, ignore it unless we are debugging. + + if ( method_h == active_method_h ) + { + DEBUG_MSG(DEBUG_NAME,method_h,"self preemption of active method"); + } + + // THE CALLER IS A METHOD: + // + // (a) Set the current process information to our method. + // (b) Invoke our method directly by-passing the run queue. + // (c) Restore the process info to the caller. + // (d) Check to see if the calling method should throw an exception + // because of activity that occurred during the preemption. + + else if ( active_method_h != NULL ) + { + caller_info = m_curr_proc_info; + DEBUG_MSG( DEBUG_NAME, method_h, + "preempting active method with method" ); + sc_get_curr_simcontext()->set_curr_proc( (sc_process_b*)method_h ); + method_h->run_process(); + sc_get_curr_simcontext()->set_curr_proc((sc_process_b*)active_method_h); + active_method_h->check_for_throws(); + } + + // CALLER IS A THREAD: + // + // (a) Use an invocation thread to execute the method. + + else if ( active_thread_h != NULL ) + { + DEBUG_MSG( DEBUG_NAME, method_h, + "preempting active thread with method" ); + m_method_invoker_p->invoke_method(method_h); + } + + // CALLER IS THE SIMULATOR: + // + // That is not allowed. + + else + { + caller_info = m_curr_proc_info; + DEBUG_MSG( DEBUG_NAME, method_h, + "preempting no active process with method" ); + sc_get_curr_simcontext()->set_curr_proc( (sc_process_b*)method_h ); + method_h->run_process(); + m_curr_proc_info = caller_info; + } +} + +//------------------------------------------------------------------------------ +//"sc_simcontext::requeue_current_process" +// +// This method requeues the current process at the beginning of the run queue +// if it is a thread. This is called by sc_process_handle::throw_it() to assure +// that a thread that is issuing a throw will execute immediately after the +// processes it notifies via the throw. +//------------------------------------------------------------------------------ +void sc_simcontext::requeue_current_process() +{ + sc_thread_handle thread_p; + thread_p = DCAST<sc_thread_handle>(get_curr_proc_info()->process_handle); + if ( thread_p ) + { + execute_thread_next( thread_p ); + } +} + +//------------------------------------------------------------------------------ +//"sc_simcontext::suspend_current_process" +// +// This method suspends the current process if it is a thread. This is called +// by sc_process_handle::throw_it() to allow the processes that have received +// a throw to execute. +//------------------------------------------------------------------------------ +void sc_simcontext::suspend_current_process() +{ + sc_thread_handle thread_p; + thread_p = DCAST<sc_thread_handle>(get_curr_proc_info()->process_handle); + if ( thread_p ) + { + thread_p->suspend_me(); + } +} + +void +sc_simcontext::trace_cycle( bool delta_cycle ) +{ + int size; + if( ( size = m_trace_files.size() ) != 0 ) { + sc_trace_file** l_trace_files = &m_trace_files[0]; + int i = size - 1; + do { + l_trace_files[i]->cycle( delta_cycle ); + } while( -- i >= 0 ); + } +} + +// ---------------------------------------------------------------------------- + +#if 1 +#ifdef PURIFY + static sc_simcontext sc_default_global_context; + sc_simcontext* sc_curr_simcontext = &sc_default_global_context; +#else + sc_simcontext* sc_curr_simcontext = 0; + sc_simcontext* sc_default_global_context = 0; +#endif +#else +// Not MT-safe! +static sc_simcontext* sc_curr_simcontext = 0; + + +sc_simcontext* +sc_get_curr_simcontext() +{ + if( sc_curr_simcontext == 0 ) { +#ifdef PURIFY + static sc_simcontext sc_default_global_context; + sc_curr_simcontext = &sc_default_global_context; +#else + static sc_simcontext* sc_default_global_context = new sc_simcontext; + sc_curr_simcontext = sc_default_global_context; +#endif + } + return sc_curr_simcontext; +} +#endif // 0 + +// Generates unique names within each module. + +const char* +sc_gen_unique_name( const char* basename_, bool preserve_first ) +{ + sc_simcontext* simc = sc_get_curr_simcontext(); + sc_module* curr_module = simc->hierarchy_curr(); + if( curr_module != 0 ) { + return curr_module->gen_unique_name( basename_, preserve_first ); + } else { + sc_process_b* curr_proc_p = sc_get_current_process_b(); + if ( curr_proc_p ) + { + return curr_proc_p->gen_unique_name( basename_, preserve_first ); + } + else + { + return simc->gen_unique_name( basename_, preserve_first ); + } + } +} + +// Get a handle for the current process +// +// Note that this method should not be called if the current process is +// in the act of being deleted, it will mess up the reference count management +// of sc_process_b instance the handle represents. Instead, use the a +// pointer to the raw sc_process_b instance, which may be acquired via +// sc_get_current_process_b(). + +sc_process_handle +sc_get_current_process_handle() +{ + return ( sc_is_running() ) ? + sc_process_handle(sc_get_current_process_b()) : + sc_get_last_created_process_handle(); +} + +// THE FOLLOWING FUNCTION IS DEPRECATED IN 2.1 +sc_process_b* +sc_get_curr_process_handle() +{ + static bool warn=true; + if ( warn ) + { + warn = false; + SC_REPORT_INFO(SC_ID_IEEE_1666_DEPRECATION_, + "sc_get_curr_process_handle deprecated use sc_get_current_process_handle" + ); + } + + return sc_get_curr_simcontext()->get_curr_proc_info()->process_handle; +} + +// Return indication if there are more processes to execute in this delta phase + +bool +sc_simcontext::pending_activity_at_current_time() const +{ + return ( m_delta_events.size() != 0) || + ( m_runnable->is_initialized() && !m_runnable->is_empty() ) || + m_prim_channel_registry->pending_updates(); +} + +// Return time of next activity. + +sc_time sc_time_to_pending_activity( const sc_simcontext* simc_p ) +{ + // If there is an activity pending at the current time + // return a delta of zero. + + sc_time result=SC_ZERO_TIME; // time of pending activity. + + if ( simc_p->pending_activity_at_current_time() ) + { + return result; + } + + // Any activity will take place in the future pick up the next event's time. + + else + { + result = simc_p->max_time(); + simc_p->next_time(result); + result -= sc_time_stamp(); + } + return result; +} + +// Set the random seed for controlled randomization -- not yet implemented + +void +sc_set_random_seed( unsigned int ) +{ + SC_REPORT_WARNING( SC_ID_NOT_IMPLEMENTED_, + "void sc_set_random_seed( unsigned int )" ); +} + + +// +---------------------------------------------------------------------------- +// |"sc_start" +// | +// | This function starts, or restarts, the execution of the simulator. +// | +// | Arguments: +// | duration = the amount of time the simulator should execute. +// | p = event starvation policy. +// +---------------------------------------------------------------------------- +void +sc_start( const sc_time& duration, sc_starvation_policy p ) +{ + sc_simcontext* context_p; // current simulation context. + sc_time entry_time; // simulation time upon entry. + sc_time exit_time; // simulation time to set upon exit. + sc_dt::uint64 starting_delta; // delta count upon entry. + int status; // current simulation status. + + // Set up based on the arguments passed to us: + + context_p = sc_get_curr_simcontext(); + starting_delta = sc_delta_count(); + entry_time = context_p->m_curr_time; + if ( p == SC_RUN_TO_TIME ) + exit_time = context_p->m_curr_time + duration; + + // called with duration = SC_ZERO_TIME for the first time + static bool init_delta_or_pending_updates = + ( starting_delta == 0 && exit_time == SC_ZERO_TIME ); + + // If the simulation status is bad issue the appropriate message: + + status = context_p->sim_status(); + if( status != SC_SIM_OK ) + { + if ( status == SC_SIM_USER_STOP ) + SC_REPORT_ERROR(SC_ID_SIMULATION_START_AFTER_STOP_, ""); + if ( status == SC_SIM_ERROR ) + SC_REPORT_ERROR(SC_ID_SIMULATION_START_AFTER_ERROR_, ""); + return; + } + + if ( context_p->m_prim_channel_registry->pending_updates() ) + init_delta_or_pending_updates = true; + + // If the simulation status is good perform the simulation: + + context_p->simulate( duration ); + + // Re-check the status: + + status = context_p->sim_status(); + + // Update the current time to the exit time if that is the starvation + // policy: + + if ( p == SC_RUN_TO_TIME && !context_p->m_paused && status == SC_SIM_OK ) + { + context_p->m_curr_time = exit_time; + } + + // If there was no activity and the simulation clock did not move warn + // the user, except if we're in a first sc_start(SC_ZERO_TIME) for + // initialisation (only) or there have been pending updates: + + if ( !init_delta_or_pending_updates && + starting_delta == sc_delta_count() && + context_p->m_curr_time == entry_time && + status == SC_SIM_OK ) + { + SC_REPORT_WARNING(SC_ID_NO_SC_START_ACTIVITY_, ""); + } + + // reset init/update flag for subsequent calls + init_delta_or_pending_updates = false; +} + +void +sc_start() +{ + sc_start( sc_max_time() - sc_time_stamp(), + SC_EXIT_ON_STARVATION ); +} + +// for backward compatibility with 1.0 +#if 0 +void +sc_start( double duration ) // in default time units +{ + static bool warn_sc_start=true; + if ( warn_sc_start ) + { + warn_sc_start = false; + SC_REPORT_INFO(SC_ID_IEEE_1666_DEPRECATION_, + "sc_start(double) deprecated, use sc_start(sc_time) or sc_start()"); + } + + if( duration == -1 ) // simulate forever + { + sc_start( + sc_time(~sc_dt::UINT64_ZERO, false) - sc_time_stamp() ); + } + else + { + sc_start( sc_time( duration, true ) ); + } +} +#endif // + +void +sc_stop() +{ + sc_get_curr_simcontext()->stop(); +} + + +// The following function is deprecated in favor of sc_start(SC_ZERO_TIME): + +void +sc_initialize() +{ + static bool warning_initialize = true; + + if ( warning_initialize ) + { + warning_initialize = false; + SC_REPORT_INFO(SC_ID_IEEE_1666_DEPRECATION_, + "sc_initialize() is deprecated: use sc_start(SC_ZERO_TIME)" ); + } + sc_get_curr_simcontext()->initialize(); +} + +// The following function has been deprecated in favor of sc_start(duration): + +void +sc_cycle( const sc_time& duration ) +{ + static bool warning_cycle = true; + + if ( warning_cycle ) + { + warning_cycle = false; + SC_REPORT_INFO(SC_ID_IEEE_1666_DEPRECATION_, + "sc_cycle is deprecated: use sc_start(sc_time)" ); + } + sc_get_curr_simcontext()->cycle( duration ); +} + +sc_event* sc_find_event( const char* name ) +{ + return sc_get_curr_simcontext()->get_object_manager()->find_event( name ); +} + +sc_object* sc_find_object( const char* name ) +{ + return sc_get_curr_simcontext()->get_object_manager()->find_object( name ); +} + + +const sc_time& +sc_max_time() +{ + return sc_get_curr_simcontext()->max_time(); +} + +const sc_time& +sc_time_stamp() +{ + return sc_get_curr_simcontext()->time_stamp(); +} + +double +sc_simulation_time() +{ + static bool warn_simulation_time=true; + if ( warn_simulation_time ) + { + warn_simulation_time=false; + SC_REPORT_INFO(SC_ID_IEEE_1666_DEPRECATION_, + "sc_simulation_time() is deprecated use sc_time_stamp()" ); + } + return sc_get_curr_simcontext()->time_stamp().to_default_time_units(); +} + +void +sc_defunct_process_function( sc_module* ) +{ + // This function is pointed to by defunct sc_thread_process'es and + // sc_cthread_process'es. In a correctly constructed world, this + // function should never be called; hence the assert. + assert( false ); +} + +//------------------------------------------------------------------------------ +//"sc_set_stop_mode" +// +// This function sets the mode of operation when sc_stop() is called. +// mode = SC_STOP_IMMEDIATE or SC_STOP_FINISH_DELTA. +//------------------------------------------------------------------------------ +void sc_set_stop_mode(sc_stop_mode mode) +{ + if ( sc_is_running() ) + { + SC_REPORT_ERROR(SC_ID_STOP_MODE_AFTER_START_,""); + } + else + { + switch( mode ) + { + case SC_STOP_IMMEDIATE: + case SC_STOP_FINISH_DELTA: + stop_mode = mode; + break; + default: + break; + } + } +} + +sc_stop_mode +sc_get_stop_mode() +{ + return stop_mode; +} + +bool sc_is_unwinding() +{ + return sc_get_current_process_handle().is_unwinding(); +} + +// The IEEE 1666 Standard for 2011 designates that the treatment of +// certain process control interactions as being "implementation dependent". +// These interactions are: +// (1) What happens when a resume() call is performed on a disabled, +// suspended process. +// (2) What happens when sync_reset_on() or sync_reset_off() is called +// on a suspended process. +// (3) What happens when the value specified in a reset_signal_is() +// call changes value while a process is suspended. +// +// By default this Proof of Concept implementation reports an error +// for these interactions. However, the implementation also provides +// a non-error treatment. The non-error treatment for the interactions is: +// (1) A resume() call performed on a disabled, suspended process will +// mark the process as no longer suspended, and if it is capable +// of execution (not waiting on any events) it will be placed on +// the queue of runnable processes. See the state diagram below. +// (2) A call to sync_reset_on() or sync_reset_off() will set or clear +// the synchronous reset flag. Whether the process is in reset or +// not will be determined when the process actually executes by +// looking at the flag's value at that time. +// (3) If a suspended process has a reset_signal_is() specification +// the value of the reset variable at the time of its next execution +// will determine whether it is in reset or not. +// +// TO GET THE NON-ERROR BEHAVIOR SET THE VARIABLE BELOW TO TRUE. +// +// This can be done in this source before you build the library, or you +// can use an assignment as the first statement in your sc_main() function: +// sc_core::sc_allow_process_control_corners = true; + +bool sc_allow_process_control_corners = false; + +// The state transition diagram for the interaction of disable and suspend +// when sc_allow_process_control_corners is true is shown below: +// +// ...................................................................... +// . ENABLED . DISABLED . +// . . . +// . +----------+ disable +----------+ . +// . +------------>| |-------.-------->| | . +// . | | runnable | . | runnable | . +// . | +-------| |<------.---------| |------+ . +// . | | +----------+ enable +----------+ | . +// . | | | ^ . | ^ | . +// . | | suspend | | resume . suspend | | resume | . +// . | | V | . V | | . +// . | | +----------+ disable +----------+ | . +// . | | | suspend |-------.-------->| suspend | | . +// . t | r | | | . | | | r . +// . r | u | | ready |<------.---------| ready | | u . +// . i | n | +----------+ enable +----------+ | n . +// . g | / | ^ . | / . +// . g | w | trigger| . | w . +// . e | a | | . | a . +// . r | i | +----------+ disable +----------+ | i . +// . | t | | suspend |-------.-------->| suspend | | t . +// . | | | | . | | | . +// . | | | waiting |<------.---------| waiting | | . +// . | | +----------+ enable +----------+ | . +// . | | | ^ . | ^ | . +// . | | suspend | | resume . suspend | | resume | . +// . | | V | . V | | . +// . | | +----------+ disable +----------+ | . +// . | +------>| |-------.-------->| | | . +// . | | waiting | . | waiting | | . +// . +-------------| |<------.---------| |<-----+ . +// . +----------+ enable +----------+ . +// . . . +// ...................................................................... + +// ---------------------------------------------------------------------------- + +static std::ostream& +print_status_expression( std::ostream& os, sc_status s ); + +// utility helper to print a simulation status +std::ostream& operator << ( std::ostream& os, sc_status s ) +{ + // print primitive values + switch(s) + { +# define PRINT_STATUS( Status ) \ + case Status: { os << #Status; } break + + PRINT_STATUS( SC_UNITIALIZED ); + PRINT_STATUS( SC_ELABORATION ); + PRINT_STATUS( SC_BEFORE_END_OF_ELABORATION ); + PRINT_STATUS( SC_END_OF_ELABORATION ); + PRINT_STATUS( SC_START_OF_SIMULATION ); + + PRINT_STATUS( SC_RUNNING ); + PRINT_STATUS( SC_PAUSED ); + PRINT_STATUS( SC_STOPPED ); + PRINT_STATUS( SC_END_OF_SIMULATION ); + + PRINT_STATUS( SC_END_OF_INITIALIZATION ); +// PRINT_STATUS( SC_END_OF_EVALUATION ); + PRINT_STATUS( SC_END_OF_UPDATE ); + PRINT_STATUS( SC_BEFORE_TIMESTEP ); + + PRINT_STATUS( SC_STATUS_ANY ); + +# undef PRINT_STATUS + default: + + if( s & SC_STATUS_ANY ) // combination of status bits + print_status_expression( os, s ); + else // invalid number, print hex value + os << "0x" << std::hex << +s; + } + + return os; +} + +// pretty-print a combination of sc_status bits (i.e. a callback mask) +static std::ostream& +print_status_expression( std::ostream& os, sc_status s ) +{ + std::vector<sc_status> bits; + unsigned is_set = SC_ELABORATION; + + // collect bits + while( is_set <= SC_STATUS_LAST ) + { + if( s & is_set ) + bits.push_back( (sc_status)is_set ); + is_set <<= 1; + } + if( s & ~SC_STATUS_ANY ) // remaining bits + bits.push_back( (sc_status)( s & ~SC_STATUS_ANY ) ); + + // print expression + std::vector<sc_status>::size_type i=0, n=bits.size(); + if ( n>1 ) + os << "("; + for( ; i<n-1; ++i ) + os << bits[i] << "|"; + os << bits[i]; + if ( n>1 ) + os << ")"; + return os; +} + +} // namespace sc_core + +/***************************************************************************** + + MODIFICATION LOG - modifiers, enter your name, affiliation, date and + changes you are making here. + + Name, Affiliation, Date: Ali Dasdan, Synopsys, Inc. + Description of Modification: - Added sc_stop() detection into initial_crunch + and crunch. This makes it possible to exit out + of a combinational loop using sc_stop(). + + Name, Affiliation, Date: Andy Goodrich, Forte Design Systems 20 May 2003 + Description of Modification: - sc_stop mode + - phase callbacks + + Name, Affiliation, Date: Bishnupriya Bhattacharya, Cadence Design Systems, + 25 August 2003 + Description of Modification: - support for dynamic process + - support for sc export registry + - new member methods elaborate(), + prepare_to_simulate(), and initial_crunch() + that are invoked by initialize() in that order + - implement sc_get_last_created_process_handle() for use + before simulation starts + - remove "set_curr_proc(handle)" from + register_method_process and + register_thread_process - led to bugs + + Name, Affiliation, Date: Andy Goodrich, Forte Design Systems 04 Sep 2003 + Description of Modification: - changed process existence structures to + linked lists to eliminate exponential + execution problem with using sc_pvector. + *****************************************************************************/ +// $Log: sc_simcontext.cpp,v $ +// Revision 1.37 2011/08/29 18:04:32 acg +// Philipp A. Hartmann: miscellaneous clean ups. +// +// Revision 1.36 2011/08/26 20:46:10 acg +// Andy Goodrich: moved the modification log to the end of the file to +// eliminate source line number skew when check-ins are done. +// +// Revision 1.35 2011/08/24 22:05:51 acg +// Torsten Maehne: initialization changes to remove warnings. +// +// Revision 1.34 2011/08/04 17:15:28 acg +// Andy Goodrich: added documentation to crunch() routine. +// +// Revision 1.32 2011/07/24 11:16:36 acg +// Philipp A. Hartmann: fix reference counting on deferred deletions of +// processes. +// +// Revision 1.31 2011/07/01 18:49:07 acg +// Andy Goodrich: moved pln() from sc_simcontext.cpp to sc_ver.cpp. +// +// Revision 1.30 2011/05/09 04:07:49 acg +// Philipp A. Hartmann: +// (1) Restore hierarchy in all phase callbacks. +// (2) Ensure calls to before_end_of_elaboration. +// +// Revision 1.29 2011/04/08 22:39:09 acg +// Andy Goodrich: moved method invocation code to sc_method.h so that the +// details are hidden from sc_simcontext. +// +// Revision 1.28 2011/04/05 20:50:57 acg +// Andy Goodrich: +// (1) changes to make sure that event(), posedge() and negedge() only +// return true if the clock has not moved. +// (2) fixes for method self-resumes. +// (3) added SC_PRERELEASE_VERSION +// (4) removed kernel events from the object hierarchy, added +// sc_hierarchy_name_exists(). +// +// Revision 1.27 2011/04/05 06:14:15 acg +// Andy Goodrich: fix typo. +// +// Revision 1.26 2011/04/05 06:03:32 acg +// Philipp A. Hartmann: added code to set ready to run bit for a suspended +// process that does not have dont_initialize specified at simulation +// start up. +// +// Revision 1.25 2011/04/01 21:31:55 acg +// Andy Goodrich: make sure processes suspended before the start of execution +// don't get scheduled for initial execution. +// +// Revision 1.24 2011/03/28 13:02:52 acg +// Andy Goodrich: Changes for disable() interactions. +// +// Revision 1.23 2011/03/12 21:07:51 acg +// Andy Goodrich: changes to kernel generated event support. +// +// Revision 1.22 2011/03/07 17:38:43 acg +// Andy Goodrich: tightening up of checks for undefined interaction between +// synchronous reset and suspend. +// +// Revision 1.21 2011/03/06 19:57:11 acg +// Andy Goodrich: refinements for the illegal suspend - synchronous reset +// interaction. +// +// Revision 1.20 2011/03/06 15:58:50 acg +// Andy Goodrich: added escape to turn off process control corner case +// checks. +// +// Revision 1.19 2011/03/05 04:45:16 acg +// Andy Goodrich: moved active process calculation to the sc_simcontext class. +// +// Revision 1.18 2011/03/05 01:39:21 acg +// Andy Goodrich: changes for named events. +// +// Revision 1.17 2011/02/18 20:27:14 acg +// Andy Goodrich: Updated Copyrights. +// +// Revision 1.16 2011/02/17 19:53:28 acg +// Andy Goodrich: eliminated use of ready_to_run() as part of process control +// simplification. +// +// Revision 1.15 2011/02/13 21:47:38 acg +// Andy Goodrich: update copyright notice. +// +// Revision 1.14 2011/02/11 13:25:24 acg +// Andy Goodrich: Philipp A. Hartmann's changes: +// (1) Removal of SC_CTHREAD method overloads. +// (2) New exception processing code. +// +// Revision 1.13 2011/02/08 08:42:50 acg +// Andy Goodrich: fix ordering of check for stopped versus paused. +// +// Revision 1.12 2011/02/07 19:17:20 acg +// Andy Goodrich: changes for IEEE 1666 compatibility. +// +// Revision 1.11 2011/02/02 07:18:11 acg +// Andy Goodrich: removed toggle() calls for the new crunch() toggle usage. +// +// Revision 1.10 2011/02/01 23:01:53 acg +// Andy Goodrich: removed dead code. +// +// Revision 1.9 2011/02/01 21:11:59 acg +// Andy Goodrich: +// (1) Use of new toggle_methods() and toggle_threads() run queue methods +// to make sure the thread run queue does not execute when allow preempt_me() +// is called from an SC_METHOD. +// (2) Use of execute_thread_next() to allow thread execution in the current +// delta cycle() rather than push_runnable_thread_front which executed +// in the following cycle. +// +// Revision 1.8 2011/01/25 20:50:37 acg +// Andy Goodrich: changes for IEEE 1666 2011. +// +// Revision 1.7 2011/01/19 23:21:50 acg +// Andy Goodrich: changes for IEEE 1666 2011 +// +// Revision 1.6 2011/01/18 20:10:45 acg +// Andy Goodrich: changes for IEEE1666_2011 semantics. +// +// Revision 1.5 2010/11/20 17:10:57 acg +// Andy Goodrich: reset processing changes for new IEEE 1666 standard. +// +// Revision 1.4 2010/07/22 20:02:33 acg +// Andy Goodrich: bug fixes. +// +// Revision 1.3 2008/05/22 17:06:26 acg +// Andy Goodrich: updated copyright notice to include 2008. +// +// Revision 1.2 2007/09/20 20:32:35 acg +// Andy Goodrich: changes to the semantics of throw_it() to match the +// specification. A call to throw_it() will immediately suspend the calling +// thread until all the throwees have executed. At that point the calling +// thread will be restarted before the execution of any other threads. +// +// Revision 1.1.1.1 2006/12/15 20:20:05 acg +// SystemC 2.3 +// +// Revision 1.21 2006/08/29 23:37:13 acg +// Andy Goodrich: Added check for negative time. +// +// Revision 1.20 2006/05/26 20:33:16 acg +// Andy Goodrich: changes required by additional platform compilers (i.e., +// Microsoft VC++, Sun Forte, HP aCC). +// +// Revision 1.19 2006/05/08 17:59:52 acg +// Andy Goodrich: added a check before m_curr_time is set to make sure it +// is not set to a time before its current value. This will treat +// sc_event.notify( ) calls with negative times as calls with a zero time. +// +// Revision 1.18 2006/04/20 17:08:17 acg +// Andy Goodrich: 3.0 style process changes. +// +// Revision 1.17 2006/04/11 23:13:21 acg +// Andy Goodrich: Changes for reduced reset support that only includes +// sc_cthread, but has preliminary hooks for expanding to method and thread +// processes also. +// +// Revision 1.16 2006/03/21 00:00:34 acg +// Andy Goodrich: changed name of sc_get_current_process_base() to be +// sc_get_current_process_b() since its returning an sc_process_b instance. +// +// Revision 1.15 2006/03/13 20:26:50 acg +// Andy Goodrich: Addition of forward class declarations, e.g., +// sc_reset, to keep gcc 4.x happy. +// +// Revision 1.14 2006/02/02 23:42:41 acg +// Andy Goodrich: implemented a much better fix to the sc_event_finder +// proliferation problem. This new version allocates only a single event +// finder for each port for each type of event, e.g., pos(), neg(), and +// value_change(). The event finder persists as long as the port does, +// which is what the LRM dictates. Because only a single instance is +// allocated for each event type per port there is not a potential +// explosion of storage as was true in the 2.0.1/2.1 versions. +// +// Revision 1.13 2006/02/02 21:29:10 acg +// Andy Goodrich: removed the call to sc_event_finder::free_instances() that +// was in end_of_elaboration(), leaving only the call in clean(). This is +// because the LRM states that sc_event_finder instances are persistent as +// long as the sc_module hierarchy is valid. +// +// Revision 1.12 2006/02/02 21:09:50 acg +// Andy Goodrich: added call to sc_event_finder::free_instances in the clean() +// method. +// +// Revision 1.11 2006/02/02 20:43:14 acg +// Andy Goodrich: Added an existence linked list to sc_event_finder so that +// the dynamically allocated instances can be freed after port binding +// completes. This replaces the individual deletions in ~sc_bind_ef, as these +// caused an exception if an sc_event_finder instance was used more than +// once, due to a double freeing of the instance. +// +// Revision 1.10 2006/01/31 21:43:26 acg +// Andy Goodrich: added comments in constructor to highlight environmental +// overrides section. +// +// Revision 1.9 2006/01/26 21:04:54 acg +// Andy Goodrich: deprecation message changes and additional messages. +// +// Revision 1.8 2006/01/25 00:31:19 acg +// Andy Goodrich: Changed over to use a standard message id of +// SC_ID_IEEE_1666_DEPRECATION for all deprecation messages. +// +// Revision 1.7 2006/01/24 20:49:05 acg +// Andy Goodrich: changes to remove the use of deprecated features within the +// simulator, and to issue warning messages when deprecated features are used. +// +// Revision 1.6 2006/01/19 00:29:52 acg +// Andy Goodrich: Yet another implementation for signal write checking. This +// one uses an environment variable SC_SIGNAL_WRITE_CHECK, that when set to +// DISABLE will disable write checking on signals. +// +// Revision 1.5 2006/01/13 18:44:30 acg +// Added $Log to record CVS changes into the source. +// +// Revision 1.4 2006/01/03 23:18:44 acg +// Changed copyright to include 2006. +// +// Revision 1.3 2005/12/20 22:11:10 acg +// Fixed $Log lines. +// +// Revision 1.2 2005/12/20 22:02:30 acg +// Changed where delta cycles are incremented to match IEEE 1666. Added the +// event_occurred() method to hide how delta cycle comparisions are done within +// sc_simcontext. Changed the boolean update_phase to an enum that shows all +// the phases. +// Taf! |