Chapter 2: Core Modeling

Reset and Clocked Processes

How to model reset, clocked behavior, dont_initialize(), async_reset_signal_is(), and reset_signal_is().

Reset modeling looks simple until the model mixes methods, threads, clocks, and initialization. Be explicit about what kind of reset you are modeling. According to the IEEE 1666 LRM Section 5.2.2, resets can be synchronous or asynchronous, and the behavior applies differently depending on the process type.

Here is a complete compilable example demonstrating both method-based synchronous resets and thread-based asynchronous resets.

#include <systemc>
 
using namespace sc_core;
 
SC_MODULE(ResetDemo) {
  sc_in<bool> clk{"clk"};
  sc_in<bool> rst_sync{"rst_sync"};
  sc_in<bool> rst_async{"rst_async"};
  
  sc_out<int> count_method{"count_method"};
  sc_out<int> count_thread{"count_thread"};
 
  SC_CTOR(ResetDemo) {
    // 1. Method-Based Clocked Logic
    SC_METHOD(tick_method);
    sensitive << clk.pos();
    dont_initialize();
 
    // 2. Thread-Based Clocked Logic with explicit reset routing
    SC_THREAD(run_thread);
    sensitive << clk.pos();
    async_reset_signal_is(rst_async, true);
  }
 
  // Synchronous Reset modeled inside the body
  void tick_method() {
    if (rst_sync.read()) {
      count_method.write(0);
      return;
    }
    count_method.write(count_method.read() + 1);
  }
 
  // Asynchronous Reset handled by the kernel
  void run_thread() {
    // This code block acts as the reset initialization
    count_thread.write(0);
    wait();
 
    while (true) {
      // Normal clocked behavior
      count_thread.write(count_thread.read() + 1);
      wait();
    }
  }
};
 
int sc_main(int argc, char* argv[]) {
  sc_clock clk("clk", 10, SC_NS);
  sc_signal<bool> rst_sync("rst_sync");
  sc_signal<bool> rst_async("rst_async");
  sc_signal<int> count_m("count_m"), count_t("count_t");
 
  ResetDemo demo("demo");
  demo.clk(clk);
  demo.rst_sync(rst_sync);
  demo.rst_async(rst_async);
  demo.count_method(count_m);
  demo.count_thread(count_t);
 
  // Assert both resets
  rst_sync.write(true);
  rst_async.write(true);
  sc_start(15, SC_NS);
 
  // De-assert and run
  rst_sync.write(false);
  rst_async.write(false);
  sc_start(50, SC_NS);
  
  // Assert async reset mid-flight
  rst_async.write(true);
  sc_start(20, SC_NS);
 
  return 0;
}

Method-Based Clocked Logic

A common method process reacts to a clock edge. dont_initialize() matters because SystemC normally initializes method processes once before simulation time advances. For clocked logic, that time-zero call is often not desired.

For a method process, synchronous reset is usually modeled explicitly in the body, checking the reset signal state before updating logic.

Thread-Based Clocked Logic

Threads can express sequential control more naturally. The exact reset behavior depends on the process kind and reset declaration. When using async_reset_signal_is(rst, true) or reset_signal_is(rst, true), the simulation kernel tracks the reset signal. If the signal goes active, the kernel throws a specific C++ exception inside the process to immediately unwind the stack and jump back to the beginning of the run method!

Avoid Hidden State Surprises

Local C++ variables inside a thread preserve state across waits:

void run() {
  unsigned local_count = 0;
  while (true) {
    out.write(local_count++);
    wait();
  }
}

If the reset restarts the process via async_reset_signal_is, local variables inside the while loop will be destroyed, and the process restarts from the top of the function where local_count = 0 is executed again.

Modeling Advice

Keep reset code boring:

  • Put all reset assignments in one obvious branch.
  • Decide whether reset affects only architectural state or also modeling statistics.
  • Test reset assertion, deassertion, and reset during activity (mid-flight).

Comments and Corrections