
//******************************************************************************
//
//  The following code is derived, directly or indirectly, from the SystemC
//  source code Copyright (c) 1996-2014 by all Contributors.
//  All Rights reserved.
//
//  The contents of this file are subject to the restrictions and limitations
//  set forth in the SystemC Open Source License (the "License");
//  You may not use this file except in compliance with such restrictions and
//  limitations. You may obtain instructions on how to receive a copy of the
//  License at http://www.accellera.org/. Software distributed by Contributors
//  under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
//  ANY KIND, either express or implied. See the License for the specific
//  language governing rights and limitations under the License.
//******************************************************************************

// sc_pause.cpp -- test for 
//
//  Original Author: John Aynsley, Doulos, Inc.
//
// MODIFICATION LOG - modifiers, enter your name, affiliation, date and
//
// $Log: sc_pause.cpp,v $
// Revision 1.2  2011/05/08 19:18:46  acg
//  Andy Goodrich: remove extraneous + prefixes from git diff.
//

// sc_pause, sc_get_status, sc_is_running

#define SC_INCLUDE_DYNAMIC_PROCESSES

#include <systemc>
using namespace sc_core;
using std::cout;
using std::endl;
using std::hex;

struct my_interface: virtual sc_interface
{
  virtual void schedule_events_while_paused() = 0;
};

struct M: sc_module, private my_interface
{
  sc_export<my_interface> xport;
  
  SC_CTOR(M)
  {
    cout << "sc_get_status() == " << hex << sc_get_status() << " CTOR in " << name() << endl;
    sc_assert( sc_get_status() == SC_BEFORE_END_OF_ELABORATION );
    sc_assert( sc_is_running() == false );
    
    xport.bind(*this);
    
    sc_spawn(sc_bind(&M::T, this));
    
    SC_THREAD(on_immed_event);
    SC_THREAD(on_delta_event);
    SC_THREAD(on_timed_event);
  }
  
  sc_signal<int> sig1, sig2, sig3, sig4;
  sc_event immed_event;
  sc_event delta_event;
  sc_event timed_event;

  void before_end_of_elaboration()
  {
    cout << "sc_get_status() == " << hex << sc_get_status() << " before_end_of_elaboration in " << name() << endl;
    sc_assert( sc_get_status() == SC_BEFORE_END_OF_ELABORATION );
    sc_assert( sc_is_running() == false );
    sig1.write(1);
    timed_event.notify(1234, SC_NS);
    sc_pause();  // Should be ignored
  }
  
  void end_of_elaboration()
  {
    cout << "sc_get_status() == " << hex << sc_get_status() << " end_of_elaboration in " << name() << endl;
    sc_assert( sc_get_status() == SC_END_OF_ELABORATION );
    sc_assert( sc_is_running() == false );
    sig2.write(2);
    sc_pause();  // Should be ignored
  }
  
  void start_of_simulation()
  {
    cout << "sc_get_status() == " << hex << sc_get_status() << " start_of_simulation in " << name() << endl;
    sc_assert( sc_get_status() == SC_START_OF_SIMULATION );
    sc_assert( sc_is_running() == false );
    sig3.write(3);
    sig4.write(0);
    sc_pause();  // Should be ignored
  }
  
  void end_of_simulation()
  {
    cout << "sc_get_status() == " << hex << sc_get_status() << " end_of_simulation in " << name() << endl;;
    sc_assert( sc_get_status() == SC_END_OF_SIMULATION );
    sc_assert( sc_is_running() == false );
  }
  
  void T()
  {
    sc_assert( sig1.read() == 1 );
    sc_assert( sig2.read() == 2 );
    sc_assert( sig3.read() == 3 );
  }
  
  void on_immed_event()
  {
    wait(immed_event);
    cout << "on_immed_event() awoke\n";

    // Should run in 1st eval phase after pause
    sc_assert( sig4.read() == 0 );
  }
  
  void on_delta_event()
  {
    wait(delta_event);
    cout << "on_delta_event() awoke\n";
    
    // Should run in 2nd eval phase after pause
    sc_assert( sig4.read() == 4 );
  }
  
  void on_timed_event()
  {
    wait(timed_event);
    cout << "on_timed_event() awoke\n";
    
    sc_assert( sig1.read() == 1 );
    sc_assert( sig2.read() == 2 );
    sc_assert( sig3.read() == 3 );
    sc_assert( sig4.read() == 0 );
    sc_assert( sc_time_stamp() == sc_time(1234, SC_NS) );
  }

private:
  
  void schedule_events_while_paused()
  {
    sig4.write(4);
    immed_event.notify();
    delta_event.notify(SC_ZERO_TIME);
    
    // Should be able to instantiate an sc_object while paused
    mut = new sc_mutex("mut");
    sem = new sc_semaphore("sem", 1);
  }
  
  sc_mutex* mut;
  sc_semaphore* sem;
};

SC_MODULE(Top)
{
  SC_CTOR(Top)
  {
    cout << "sc_get_status() == " << hex << sc_get_status() << " CTOR in " << name() << endl;
    sc_assert( sc_get_status() == SC_ELABORATION );
    sc_assert( sc_is_running() == false );
    SC_THREAD(T);
    
    sc_spawn_options opt;
      opt.spawn_method();
      opt.set_sensitivity( &timed_ev );
      opt.set_sensitivity( &delta_ev );
      opt.dont_initialize();
    sc_spawn(sc_bind(&Top::ev_handler, this), "ev_handler", &opt);
    
    SC_METHOD(immed_ev_handler);
      sensitive << immed_ev;
      dont_initialize();

    immed_ev_delta = 0;
    sc_assert( sc_delta_count() == 0 );
  }
  
  ~Top()
  {
    sc_assert( sc_get_status() == SC_STOPPED );
    sc_assert( sc_is_running() == false );
  }

  M* m;
  sc_event timed_ev;
  sc_event delta_ev;
  sc_event immed_ev;
  unsigned immed_ev_delta;
  sc_signal<int> sig;
    
  void before_end_of_elaboration()
  {
    cout << "sc_get_status() == " << hex << sc_get_status() << " before_end_of_elaboration in " << name() << endl;
    sc_assert( sc_get_status() == SC_BEFORE_END_OF_ELABORATION );
    sc_assert( sc_is_running() == false );

    m = new M("m");
  }
  
  void end_of_elaboration()
  {
    cout << "sc_get_status() == " << hex << sc_get_status() << " end_of_elaboration in " << name() << endl;
    sc_assert( sc_get_status() == SC_END_OF_ELABORATION );
    sc_assert( sc_is_running() == false );
  }
  
  void start_of_simulation()
  {
    cout << "sc_get_status() == " << hex << sc_get_status() << " start_of_simulation in " << name() << endl;
    sc_assert( sc_get_status() == SC_START_OF_SIMULATION );
    sc_assert( sc_is_running() == false );
  }
  
  void end_of_simulation()
  {
    cout << "sc_get_status() == " << hex << sc_get_status() << " end_of_simulation in " << name() << endl;
    sc_assert( sc_get_status() == SC_END_OF_SIMULATION );
    sc_assert( sc_is_running() == false );
    sc_assert( immed_ev_delta == 999 );
  }
  
  void T()
  {
    cout << "sc_get_status() == " << hex << sc_get_status() << " PROCESS in " << name() << endl;
    sc_assert( sc_delta_count() == 0 );
    sc_assert( sig.read()      == 42 );
    sc_assert( sc_get_status() == SC_RUNNING );
    sc_assert( sc_is_running() == true );
    wait(timed_ev);
    sc_assert( sc_time_stamp() == sc_time(42, SC_US) );
    sc_assert( sc_get_status() == SC_RUNNING );
    sc_assert( sc_is_running() == true );
    
    sc_pause();
    cout << "sc_get_status() == " << hex << sc_get_status() << " PROCESS after sc_pause in " << name() << endl;
    sc_assert( sc_time_stamp() == sc_time(42, SC_US) );
    sc_assert( sc_get_status() == SC_RUNNING );
    sc_assert( sc_is_running() == true );
    wait(SC_ZERO_TIME);
    sc_assert( sc_get_status() == SC_RUNNING );
    sc_assert( sc_is_running() == true );
    
    sc_pause();
    cout << "sc_get_status() == " << hex << sc_get_status() << " PROCESS after sc_pause in " << name() << endl;
    sc_assert( sc_time_stamp() == sc_time(42, SC_US) );
    sc_assert( sc_get_status() == SC_RUNNING );
    sc_assert( sc_is_running() == true );
    wait(2, SC_US);
    sc_assert( sc_time_stamp() == sc_time(44, SC_US) );
    sc_assert( sc_get_status() == SC_RUNNING );
    sc_assert( sc_is_running() == true );
    
    sc_stop();
    cout << "sc_get_status() == " << hex << sc_get_status() << " PROCESS after sc_stop() in " << name() << endl;;
    sc_assert( sc_time_stamp() == sc_time(44, SC_US) );
    sc_assert( sc_get_status() == SC_RUNNING );
    sc_assert( sc_is_running() == true );
    
    sc_pause();
    sc_assert( sc_get_status() == SC_RUNNING );
  }
  
  void ev_handler()
  {
    cout << "sc_get_status() == " << hex << sc_get_status() << " METHOD in " << name() << endl;
    sc_assert( sc_get_status() == SC_RUNNING );
    sc_assert( sig.read()      == 42 );

    static bool first = true;
    if (first)
    {
      sc_assert( sc_time_stamp() == SC_ZERO_TIME );
      first = false;
    }
    else
      sc_assert( sc_time_stamp() == sc_time(42, SC_US) );
  }
  
  void immed_ev_handler()
  {
    sc_assert( sc_time_stamp() == sc_time(42, SC_US) );
    sc_assert( sc_delta_count() == immed_ev_delta );
    immed_ev_delta = 999;
  }
};

void spawned_while_paused()
{
  cout << "spawned_while_paused() awoke" << endl;
  sc_assert( sc_time_stamp() == sc_time(42, SC_US) );
  sc_assert( sc_get_status() == SC_RUNNING );
  sc_assert( sc_pending_activity() == true );
}

int sc_main(int argc, char* argv[])
{
  sc_assert( sc_delta_count() == 0 );
  cout << "sc_get_status() == " << hex << sc_get_status() << " ELAB" << endl;
  sc_assert( sc_get_status() == SC_ELABORATION );
  sc_assert( sc_is_running() == false );
  Top top("top");
  
  sc_pause();  // Should be ignored
  
  // Schedule some update requests and events
  top.sig.write(42);
  top.timed_ev.notify(42, SC_US);
  top.delta_ev.notify(SC_ZERO_TIME);
  sc_assert( sc_get_status() == SC_ELABORATION );
  sc_assert( sc_delta_count() == 0 );

  sc_start();
  cout << "sc_get_status() == " << hex << sc_get_status() << " PAUSED" << endl;
  sc_assert( sc_get_status() == SC_PAUSED );
  sc_assert( sc_is_running() == true );
  sc_assert( sc_time_stamp() == sc_time(42, SC_US) );
  
  sc_start(1, SC_US);
  cout << "sc_get_status() == " << hex << sc_get_status() << " PAUSED" << endl;
  sc_assert( sc_get_status() == SC_PAUSED );
  sc_assert( sc_is_running() == true );
  sc_assert( sc_time_stamp() == sc_time(42, SC_US) );

  // Schedule an immediate notification
  top.immed_ev.notify();
  top.immed_ev_delta = sc_delta_count();

  // IMC while paused  
  top.m->xport->schedule_events_while_paused();
  
  sc_process_handle h = sc_spawn(&spawned_while_paused);
  sc_assert( h.valid() );

  sc_start();
  cout << "sc_get_status() == " << hex << sc_get_status() << " STOPPED" << endl;
  sc_assert( sc_get_status() == SC_STOPPED );
  sc_assert( sc_is_running() == false );
  sc_assert( sc_time_stamp() == sc_time(44, SC_US) );

  cout << endl << "Success" << endl;
  return 0;
}
