| /***************************************************************************** | |
| 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! |