/*****************************************************************************

  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.

 *****************************************************************************/

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

// Calling process control methods during elaboration

#include "systemc.h"

SC_MODULE(Top)
{
  SC_CTOR(Top)
  : caught_throw_it(0)
  , caught_reset(0)
  , caught_kill(0)

  , target_suspend_1_called(false)
  , target_suspend_2_called(false)
  , target_suspend_3_called(false)
  , target_suspend_4_called(false)
  , target_suspend_5_called(false)

  , target_disable_1_called(false)
  , target_disable_2_called(false)
  , target_disable_2_called_again(false)
  , target_disable_3_called(false)
  , target_disable_4_called(false)
  , target_disable_5_called(false)

  , target_sync_reset_1_called(false)
  , target_sync_reset_2_called(false)
  , target_sync_reset_3_called(false)
  , target_sync_reset_4_called(false)

  , reset_1_count(0)
  , reset_2_count(0)
  , reset_3_count(0)
  , reset_4_count(0)
  {
    SC_THREAD(ctrl);

    SC_THREAD(target_suspend_1);
      sensitive << ev;
      ts1 = sc_get_current_process_handle();

    SC_THREAD(target_suspend_2);
      dont_initialize();
      sensitive << ev;
      ts2 = sc_get_current_process_handle();

    SC_THREAD(target_suspend_3);
      dont_initialize();
      sensitive << ev;
      ts3 = sc_get_current_process_handle();

    SC_THREAD(target_suspend_4);
      dont_initialize();
      sensitive << ev;
      ts4 = sc_get_current_process_handle();
      ts4.suspend();

    SC_THREAD(target_suspend_5);
      dont_initialize();
      sensitive << ev;
      ts5 = sc_get_current_process_handle();
      ts5.suspend();
      ts5.resume();

    SC_THREAD(target_disable_1);
      sensitive << dummy_ev;
      td1 = sc_get_current_process_handle();

    SC_THREAD(target_disable_2);
      sensitive << ev;
      td2 = sc_get_current_process_handle();

    SC_THREAD(target_disable_3);
      td3 = sc_get_current_process_handle();

    SC_THREAD(target_disable_4);
      dont_initialize();
      sensitive << ev;
      td4 = sc_get_current_process_handle();
      td4.disable();

    SC_THREAD(target_disable_5);
      dont_initialize();
      sensitive << ev;
      td5 = sc_get_current_process_handle();
      td5.disable();
      td5.enable();
      td5.sync_reset_on();
      td5.sync_reset_off();

    SC_THREAD(target_sync_reset_1);
      sensitive << ev;
      tr1 = sc_get_current_process_handle();

    SC_THREAD(target_sync_reset_2);
      sensitive << ev;
      tr2 = sc_get_current_process_handle();

    SC_THREAD(target_sync_reset_3);
      dont_initialize();
      sensitive << ev;
      tr3 = sc_get_current_process_handle();

    SC_THREAD(target_sync_reset_4);
      dont_initialize();
      sensitive << ev;
      tr4 = sc_get_current_process_handle();
      tr4.sync_reset_on();

    try {
      ts2.throw_it(ex);
      sc_assert( false );
    }
    catch (const std::exception & e) {
      ++caught_throw_it;
    }

    try {
      ts2.reset();
      sc_assert( false );
    }
    catch (const std::exception & e) {
      ++caught_reset;
    }

    try {
      ts2.kill();
      sc_assert( false );
    }
    catch (const std::exception & e) {
      ++caught_kill;
    }
  }

  void before_end_of_elaboration()
  {
    ts1.suspend();
    ts2.suspend();
    ts3.suspend();

    td1.disable();
    td2.disable();
    td3.disable();

    tr1.sync_reset_on();
    tr2.sync_reset_on();
    tr3.sync_reset_on();

    try {
      ts2.throw_it(ex);
      sc_assert( false );
    }
    catch (const std::exception & e) {
      ++caught_throw_it;
    }

    try {
      ts2.reset();
      sc_assert( false );
    }
    catch (const std::exception & e) {
      ++caught_reset;
    }

    try {
      ts2.kill();
      sc_assert( false );
    }
    catch (const std::exception & e) {
      ++caught_kill;
    }
  }

  void start_of_simulation()
  {
    td3.enable();
    tr3.sync_reset_off();

    try {
      ts2.throw_it(ex);
      sc_assert( false );
    }
    catch (const std::exception & e) {
      ++caught_throw_it;
    }

    try {
      ts2.reset();
      sc_assert( false );
    }
    catch (const std::exception & e) {
      ++caught_reset;
    }

    try {
      ts2.kill();
      sc_assert( false );
    }
    catch (const std::exception & e) {
      ++caught_kill;
    }
  }

  sc_event ev, dummy_ev;

  std::exception ex;

  sc_process_handle ts1;
  sc_process_handle ts2;
  sc_process_handle ts3;
  sc_process_handle ts4;
  sc_process_handle ts5;

  sc_process_handle td1;
  sc_process_handle td2;
  sc_process_handle td3;
  sc_process_handle td4;
  sc_process_handle td5;

  sc_process_handle tr1;
  sc_process_handle tr2;
  sc_process_handle tr3;
  sc_process_handle tr4;

  int caught_throw_it;
  int caught_reset;
  int caught_kill;

  bool target_suspend_1_called;
  bool target_suspend_2_called;
  bool target_suspend_3_called;
  bool target_suspend_4_called;
  bool target_suspend_5_called;

  bool target_disable_1_called;
  bool target_disable_2_called;
  bool target_disable_2_called_again;
  bool target_disable_3_called;
  bool target_disable_4_called;
  bool target_disable_5_called;

  bool target_sync_reset_1_called;
  bool target_sync_reset_2_called;
  bool target_sync_reset_3_called;
  bool target_sync_reset_4_called;

  int reset_1_count;
  int reset_2_count;
  int reset_3_count;
  int reset_4_count;

  void ctrl()
  {
    ts3.resume();

    wait(10, SC_NS);
    ts1.resume();
    ev.notify();

    wait(10, SC_NS);
    ts2.resume();
    tr2.sync_reset_off();
    tr4.sync_reset_off();

    wait(10, SC_NS);
    td2.enable();

    wait(10, SC_NS);
    ev.notify();

    wait(10, SC_NS);
    ev.notify();
    td2.disable();

    wait(10, SC_NS);
    td4.enable();
    ts4.resume();
    ev.notify();
  }

  void target_suspend_1()
  {
    sc_assert( sc_time_stamp() == sc_time(10, SC_NS) );
    target_suspend_1_called = true;
  }

  void target_suspend_2()
  {
    sc_assert( sc_time_stamp() == sc_time(20, SC_NS) );
    target_suspend_2_called = true;
  }

  void target_suspend_3()
  {
    sc_assert( sc_time_stamp() == sc_time(10, SC_NS) );
    target_suspend_3_called = true;
  }

  void target_suspend_4()
  {
    sc_assert( sc_time_stamp() == sc_time(60, SC_NS) );
    target_suspend_4_called = true;
  }

  void target_suspend_5()
  {
    sc_assert( sc_time_stamp() == sc_time(10, SC_NS) );
    target_suspend_5_called = true;
    ts5.suspend();
  }

  void target_disable_1()
  {
    sc_assert( false );
    target_disable_1_called = true;
  }

  void target_disable_2()
  {
    sc_assert( sc_time_stamp() == sc_time(40, SC_NS) );
    target_disable_2_called = true;
    wait(); // on ev

    sc_assert( sc_time_stamp() == sc_time(50, SC_NS) );
    target_disable_2_called_again = true;
  }

  void target_disable_3()
  {
    sc_assert( sc_time_stamp() == sc_time(0, SC_NS) );
    sc_assert( sc_delta_count() == 0 );
    target_disable_3_called = true;
  }

  void target_disable_4()
  {
    sc_assert( sc_time_stamp() == sc_time(60, SC_NS) );
    target_disable_4_called = true;
  }

  void target_disable_5()
  {
    sc_assert( sc_time_stamp() == sc_time(10, SC_NS) );
    target_disable_5_called = true;
    td5.disable();
  }

  void target_sync_reset_1()
  {
    switch (reset_1_count)
    {
      case 0: sc_assert( sc_time_stamp() == sc_time(0, SC_NS) );
              sc_assert( sc_delta_count() == 0 );
              break;
      case 1: sc_assert( sc_time_stamp() == sc_time(10, SC_NS) );
              break;
      case 2: sc_assert( sc_time_stamp() == sc_time(40, SC_NS) );
              break;
      case 3: sc_assert( sc_time_stamp() == sc_time(50, SC_NS) );
              target_sync_reset_1_called = true;
              break;
    }
    ++reset_1_count;

    while (true)
    {
      wait();
      sc_assert( false );
    }
  }

  void target_sync_reset_2()
  {
    switch (reset_2_count)
    {
      case 0: sc_assert( sc_time_stamp() == sc_time(0, SC_NS) );
              sc_assert( sc_delta_count() == 0 );
              break;
      case 1: sc_assert( sc_time_stamp() == sc_time(10, SC_NS) );
              break;
      case 2: sc_assert( false );
              break;
      case 3: sc_assert( false );
              break;
    }
    ++reset_2_count;

    while (true)
    {
      wait();

    switch (reset_2_count)
    {
      case 0: sc_assert( false );
              break;
      case 1: sc_assert( false );
              break;
      case 2: sc_assert( sc_time_stamp() == sc_time(40, SC_NS) );
              break;
      case 3: sc_assert( sc_time_stamp() == sc_time(50, SC_NS) );
              target_sync_reset_2_called = true;
              break;
    }
    ++reset_2_count;

    }
  }

  void target_sync_reset_3()
  {
    switch (reset_3_count)
    {
      case 0: sc_assert( sc_time_stamp() == sc_time(10, SC_NS) );
              break;
      case 1: sc_assert( false );
              break;
      case 2: sc_assert( false );
              break;
    }
    ++reset_3_count;

    while (true)
    {
      wait();

    switch (reset_3_count)
    {
      case 0: sc_assert( false );
              break;
      case 1: sc_assert( sc_time_stamp() == sc_time(40, SC_NS) );
              break;
      case 2: sc_assert( sc_time_stamp() == sc_time(50, SC_NS) );
              target_sync_reset_3_called = true;
              break;
    }
    ++reset_3_count;

    }
  }

  void target_sync_reset_4()
  {
    switch (reset_4_count)
    {
      case 0: sc_assert( sc_time_stamp() == sc_time(10, SC_NS) );
              break;
      case 1: sc_assert( false );
              break;
      case 2: sc_assert( false );
              break;
    }
    ++reset_4_count;

    while (true)
    {
      wait();

    switch (reset_4_count)
    {
      case 0: sc_assert( false );
              break;
      case 1: sc_assert( sc_time_stamp() == sc_time(40, SC_NS) );
              break;
      case 2: sc_assert( sc_time_stamp() == sc_time(50, SC_NS) );
              target_sync_reset_4_called = true;
              break;
    }
    ++reset_4_count;

    }
  }
};

int sc_main(int argc, char* argv[])
{
  Top top("top");
  sc_start();

  sc_assert( top.caught_throw_it == 3 );
  sc_assert( top.caught_reset == 3 );
  sc_assert( top.caught_kill == 3 );

  sc_assert( top.target_suspend_1_called );
  sc_assert( top.target_suspend_2_called );
  sc_assert( top.target_suspend_3_called );
  sc_assert( top.target_suspend_4_called );
  sc_assert( top.target_suspend_5_called );

  sc_assert( !top.target_disable_1_called );
  sc_assert( top.target_disable_2_called );
  sc_assert( top.target_disable_2_called_again );
  sc_assert( top.target_disable_3_called );
  sc_assert( top.target_disable_4_called );
  sc_assert( top.target_disable_5_called );

  sc_assert( top.target_sync_reset_1_called );
  sc_assert( top.target_sync_reset_2_called );
  sc_assert( top.target_sync_reset_3_called );
  sc_assert( top.target_sync_reset_4_called );

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