//------------------------------------------------------------------------------ //"sc_method_process::throw_reset" // // This virtual method is invoked to "throw" a reset. // // If the reset is synchronous this is a no-op, except for triggering the // reset event if it is present. // // If the reset is asynchronous we: // (a) cancel any dynamic waits // (b) if it is the active process actually throw a reset exception. // (c) if it was not the active process and does not have a static // sensitivity emit an error if corner cases are to be considered // errors. // // Notes: // (1) If the process had a reset event it will have been triggered in // sc_process_b::semantics() // // Arguments: // async = true if this is an asynchronous reset. //------------------------------------------------------------------------------ void sc_method_process::throw_reset( bool async ) { // IF THE PROCESS IS CURRENTLY UNWINDING OR IS ALREADY A ZOMBIE // IGNORE THE RESET: if ( m_unwinding ) { SC_REPORT_WARNING( SC_ID_PROCESS_ALREADY_UNWINDING_, name() ); return; } if ( m_state & ps_bit_zombie ) return; // Set the throw status and if its an asynchronous reset throw an // exception: m_throw_status = async ? THROW_ASYNC_RESET : THROW_SYNC_RESET; if ( async ) { remove_dynamic_events(); if ( sc_get_current_process_b() == this ) { DEBUG_MSG(DEBUG_NAME,this,"throw_reset: throwing exception"); m_throw_status = THROW_ASYNC_RESET; throw sc_unwind_exception( this, true ); } else { DEBUG_MSG(DEBUG_NAME,this, "throw_reset: queueing this method for execution"); simcontext()->preempt_with(this); } } }
//------------------------------------------------------------------------------ //"sc_process_b::reset_process" // // This inline method changes the reset state of this object instance and // conditionally its descendants. // // Notes: // (1) It is called for sync_reset_on() and sync_reset_off(). It is not used // for signal sensitive resets, though all reset flow ends up in // reset_changed(). // // Arguments: // rt = source of the reset: // * reset_asynchronous - sc_process_handle::reset() // * reset_synchronous_off - sc_process_handle::sync_reset_off() // * reset_synchronous_on - sc_process_handle::sync_reset_on() // descendants = indication of how to process descendants. //------------------------------------------------------------------------------ void sc_process_b::reset_process( reset_type rt, sc_descendant_inclusion_info descendants ) { // PROCESS THIS OBJECT INSTANCE'S DESCENDANTS IF REQUESTED TO: if ( descendants == SC_INCLUDE_DESCENDANTS ) { const std::vector<sc_object*> children = get_child_objects(); int child_n = children.size(); for ( int child_i = 0; child_i < child_n; child_i++ ) { sc_process_b* child_p = DCAST<sc_process_b*>(children[child_i]); if ( child_p ) child_p->reset_process(rt, descendants); } } // PROCESS THIS OBJECT INSTANCE: switch (rt) { // One-shot asynchronous reset: remove dynamic sensitivity and throw: // // If this is an sc_method only throw if it is active. case reset_asynchronous: if ( sc_get_status() != SC_RUNNING ) { report_error(SC_ID_RESET_PROCESS_WHILE_NOT_RUNNING_); } else { remove_dynamic_events(); throw_reset(true); } break; // Turn on sticky synchronous reset: use standard reset mechanism. case reset_synchronous_on: if ( m_sticky_reset == false ) { m_sticky_reset = true; reset_changed( false, true ); } break; // Turn off sticky synchronous reset: use standard reset mechanism. default: if ( m_sticky_reset == true ) { m_sticky_reset = false; reset_changed( false, false ); } break; } }
//------------------------------------------------------------------------------ //"sc_process_b::disconnect_process" // // This method removes this object instance from use. It will be called by // the kill_process() methods of classes derived from it. This object instance // will be removed from any data structures it resides, other than existence. //------------------------------------------------------------------------------ void sc_process_b::disconnect_process() { int mon_n; // monitor queue size. sc_thread_handle thread_h; // This process as a thread. // IF THIS OBJECT IS PINING FOR THE FJORDS WE ARE DONE: if ( m_state & ps_bit_zombie ) return; // IF THIS IS A THREAD SIGNAL ANY MONITORS WAITING FOR IT TO EXIT: switch ( m_process_kind ) { case SC_THREAD_PROC_: case SC_CTHREAD_PROC_: thread_h = SCAST<sc_thread_handle>(this); mon_n = thread_h->m_monitor_q.size(); if ( mon_n ) { for ( int mon_i = 0; mon_i < mon_n; mon_i++ ) { thread_h->m_monitor_q[mon_i]->signal( thread_h, sc_process_monitor::spm_exit); } } break; default: break; } // REMOVE EVENT WAITS, AND REMOVE THE PROCESS FROM ITS SC_RESET: remove_dynamic_events(); remove_static_events(); for ( size_t rst_i = 0; rst_i < m_resets.size(); rst_i++ ) { m_resets[rst_i]->remove_process( this ); } m_resets.resize(0); // FIRE THE TERMINATION EVENT, MARK AS TERMINATED, AND DECREMENT THE COUNT: // // (1) We wait to set the process kind until after doing the removals // above. // (2) Decrementing the reference count will result in actual object // deletion if we hit zero. m_state = ps_bit_zombie; if ( m_term_event_p ) m_term_event_p->notify(); reference_decrement(); }
//------------------------------------------------------------------------------ //"sc_method_process::throw_user" // // This virtual method is invoked when a user exception is to be thrown. // If requested it will also throw the exception to the children of this // object instance. Since this is a method no throw will occur for this // object instance. The children will be awakened from youngest child to // eldest. // helper_p -> object to use to throw the exception. // descendants = indicator of whether this process' children should also // be suspended //------------------------------------------------------------------------------ void sc_method_process::throw_user( const sc_throw_it_helper& helper, sc_descendant_inclusion_info descendants ) { // IF THE SIMULATION IS NOT ACTUALLY RUNNING THIS IS AN ERROR: if ( sc_get_status() != SC_RUNNING ) { report_error( SC_ID_THROW_IT_WHILE_NOT_RUNNING_ ); } // IF NEEDED PROPOGATE THE THROW REQUEST THROUGH OUR DESCENDANTS: if ( descendants == SC_INCLUDE_DESCENDANTS ) { const std::vector<sc_object*> children = get_child_objects(); int child_n = children.size(); for ( int child_i = 0; child_i < child_n; child_i++ ) { sc_process_b* child_p = DCAST<sc_process_b*>(children[child_i]); if ( child_p ) { DEBUG_MSG(DEBUG_NAME,child_p,"about to throw user on"); child_p->throw_user(helper, descendants); } } } #if 0 // shouldn't we throw, if we're currently running? if ( sc_get_current_process_b() == (sc_process_b*)this ) { remove_dynamic_events(); m_throw_status = THROW_USER; if ( m_throw_helper_p != 0 ) delete m_throw_helper_p; m_throw_helper_p = helper.clone(); m_throw_helper_p->throw_it(); } // throw_it HAS NO EFFECT ON A METHOD, ISSUE A WARNING: else #endif { SC_REPORT_WARNING( SC_ID_THROW_IT_IGNORED_, name() ); } }
//------------------------------------------------------------------------------ //"sc_method_process::resume_process" // // This method resumes the execution of this process, and if requested, its // descendants. If the process was suspended and has a resumption pending it // will be dispatched in the next delta cycle. Otherwise the state will be // adjusted to indicate it is no longer suspended, but no immediate execution // will occur. //------------------------------------------------------------------------------ void sc_method_process::resume_process( sc_descendant_inclusion_info descendants ) { // IF NEEDED PROPOGATE THE RESUME REQUEST THROUGH OUR DESCENDANTS: if ( descendants == SC_INCLUDE_DESCENDANTS ) { const std::vector<sc_object*>& children = get_child_objects(); int child_n = children.size(); for ( int child_i = 0; child_i < child_n; child_i++ ) { sc_process_b* child_p = DCAST<sc_process_b*>(children[child_i]); if ( child_p ) child_p->resume_process(descendants); } } // BY DEFAULT THE CORNER CASE IS AN ERROR: if ( !sc_allow_process_control_corners && (m_state & ps_bit_disabled) && (m_state & ps_bit_suspended) ) { m_state = m_state & ~ps_bit_suspended; report_error( SC_ID_PROCESS_CONTROL_CORNER_CASE_, "call to resume() on a disabled suspended method"); } // CLEAR THE SUSPENDED BIT: m_state = m_state & ~ps_bit_suspended; // RESUME OBJECT INSTANCE: // // If this is not a self-resume and the method is ready to run then // put it on the runnable queue. if ( m_state & ps_bit_ready_to_run ) { m_state = m_state & ~ps_bit_ready_to_run; if ( next_runnable() == 0 && ( sc_get_current_process_b() != DCAST<sc_process_b*>(this) ) ) { simcontext()->push_runnable_method(this); remove_dynamic_events(); } } }
//------------------------------------------------------------------------------ //"sc_method_process::trigger_dynamic" // // This method sets up a dynamic trigger on an event. // // Notes: // (1) This method is identical to sc_thread_process::trigger_dynamic(), // but they cannot be combined as sc_process_b::trigger_dynamic() // because the signatures things like sc_event::remove_dynamic() // have different overloads for sc_method_process* and sc_thread_process*. // So if you change code here you'll also need to change it in // sc_thread_process.cpp. // // Result is true if this process should be removed from the event's list, // false if not. // // If the triggering process is the same process, the trigger is // ignored as well, unless SC_ENABLE_IMMEDIATE_SELF_NOTIFICATIONS // is defined. //------------------------------------------------------------------------------ bool sc_method_process::trigger_dynamic( sc_event* e ) { // No time outs yet, and keep gcc happy. m_timed_out = false; // Escape cases: // (a) If this method issued the notify() don't schedule it for // execution, but leave the sensitivity in place. // (b) If this method is already runnable can't trigger an event. #if ! defined( SC_ENABLE_IMMEDIATE_SELF_NOTIFICATIONS ) if( SC_UNLIKELY_( sc_get_current_process_b() == this ) ) { report_immediate_self_notification(); return false; } #endif // SC_ENABLE_IMMEDIATE_SELF_NOTIFICATIONS if( is_runnable() ) return true; // If a process is disabled then we ignore any events, leaving them enabled: // // But if this is a time out event we need to remove both it and the // event that was being waited for. if ( m_state & ps_bit_disabled ) { if ( e == m_timeout_event_p ) { remove_dynamic_events( true ); return true; } else { return false; } } // Process based on the event type and current process state: // // Every case needs to set 'rc' and continue on to the end of // this method to allow suspend processing to work correctly. switch( m_trigger_type ) { case EVENT: m_event_p = 0; m_trigger_type = STATIC; break; case AND_LIST: -- m_event_count; if ( m_event_count == 0 ) { m_event_list_p->auto_delete(); m_event_list_p = 0; m_trigger_type = STATIC; } else { return true; } break; case OR_LIST: m_event_list_p->remove_dynamic( this, e ); m_event_list_p->auto_delete(); m_event_list_p = 0; m_trigger_type = STATIC; break; case TIMEOUT: m_trigger_type = STATIC; break; case EVENT_TIMEOUT: if ( e == m_timeout_event_p ) { m_timed_out = true; m_event_p->remove_dynamic( this ); m_event_p = 0; m_trigger_type = STATIC; } else { m_timeout_event_p->cancel(); m_timeout_event_p->reset(); m_event_p = 0; m_trigger_type = STATIC; } break; case OR_LIST_TIMEOUT: if ( e == m_timeout_event_p ) { m_timed_out = true; m_event_list_p->remove_dynamic( this, e ); m_event_list_p->auto_delete(); m_event_list_p = 0; m_trigger_type = STATIC; } else { m_timeout_event_p->cancel(); m_timeout_event_p->reset(); m_event_list_p->remove_dynamic( this, e ); m_event_list_p->auto_delete(); m_event_list_p = 0; m_trigger_type = STATIC; } break; case AND_LIST_TIMEOUT: if ( e == m_timeout_event_p ) { m_timed_out = true; m_event_list_p->remove_dynamic( this, e ); m_event_list_p->auto_delete(); m_event_list_p = 0; m_trigger_type = STATIC; } else { -- m_event_count; if ( m_event_count == 0 ) { m_timeout_event_p->cancel(); m_timeout_event_p->reset(); // no need to remove_dynamic m_event_list_p->auto_delete(); m_event_list_p = 0; m_trigger_type = STATIC; } else { return true; } } break; case STATIC: { // we should never get here, but throw_it() can make it happen. SC_REPORT_WARNING(SC_ID_NOT_EXPECTING_DYNAMIC_EVENT_NOTIFY_, name()); return true; } } // If we get here then the method has satisfied its next_trigger, if its // suspended mark its state as ready to run. If its not suspended then push // it onto the runnable queue. if ( (m_state & ps_bit_suspended) ) { m_state = m_state | ps_bit_ready_to_run; } else { simcontext()->push_runnable_method(this); } return true; }