Chapter 14: SystemC

Processes: SC_METHOD vs SC_CTHREAD

How SystemC processes are interpreted by High-Level Synthesis tools.

Processes: SC_METHOD vs SC_CTHREAD

In SystemC simulation, you have three process types: SC_METHOD, SC_THREAD, and SC_CTHREAD. When it comes to High-Level Synthesis (HLS), this list is strictly narrowed.

The Ban on SC_THREAD

The standard SC_THREAD is generally not recommended for synthesis, and many strict HLS tools outright reject it.

Why? An SC_THREAD is an unconstrained coroutine. It can call wait() on any arbitrary event, at any time, with no relationship to a clock. Hardware requires a clock to schedule state transitions. Synthesizing an unconstrained SC_THREAD into a Finite State Machine (FSM) is extremely difficult and ambiguous.

Using SC_CTHREAD and SC_METHOD

Instead of SC_THREAD, HLS tools mandate the use of SC_CTHREAD (Clocked Thread) for sequential logic, and SC_METHOD for purely combinational logic.

An SC_CTHREAD is statically bound to a single clock edge during elaboration. When you call wait() inside an SC_CTHREAD, the HLS tool knows exactly what it means: "Wait exactly one clock cycle." This allows the HLS compiler to automatically extract a Finite State Machine (FSM).

An SC_METHOD must not have any internal state (no static variables) and must write to all its outputs in every possible execution path to avoid inferring latches.

Here is a complete, fully compilable example demonstrating both synthesizable process types:

#include <systemc>
 
using namespace sc_core;
 
SC_MODULE(HLS_Process_Demo) {
    sc_in_clk clk{"clk"};
    sc_in<bool> reset{"reset"};
    
    // Inputs and Outputs for Combinational Logic
    sc_in<int> a{"a"};
    sc_in<int> b{"b"};
    sc_out<int> max_val{"max_val"};
    
    // Output for Sequential Logic (FSM)
    sc_out<int> state_out{"state_out"};
 
    // 1. SC_CTHREAD for sequential FSM logic
    void clocked_logic() {
        // Reset state
        state_out.write(0);
        wait(); // Wait 1 clock cycle
        
        while (true) {
            // State 1
            state_out.write(1);
            wait(); // Wait 1 clock cycle
            
            // State 2
            state_out.write(2);
            wait(); // Wait 1 clock cycle
        }
    }
 
    // 2. SC_METHOD for purely combinational logic
    void combinational_logic() {
        // If a > b, out = a, else out = b (Combinational Mux)
        if (a.read() > b.read()) {
            max_val.write(a.read());
        } else {
            max_val.write(b.read());
        }
    }
 
    SC_CTOR(HLS_Process_Demo) {
        SC_CTHREAD(clocked_logic, clk.pos());
        reset_signal_is(reset, true); // Synchronous reset declaration
 
        SC_METHOD(combinational_logic);
        sensitive << a << b; // Sensitive to inputs, exactly like combinational hardware
    }
};
 
int sc_main(int argc, char* argv[]) {
    sc_clock clk("clk", 10, SC_NS);
    sc_signal<bool> reset("reset");
    sc_signal<int> a("a"), b("b"), max_val("max_val"), state_out("state_out");
 
    HLS_Process_Demo demo("demo");
    demo.clk(clk);
    demo.reset(reset);
    demo.a(a);
    demo.b(b);
    demo.max_val(max_val);
    demo.state_out(state_out);
 
    sc_start(50, SC_NS);
    return 0;
}

For synthesis, SC_METHOD is sensitive to changes in its inputs, exactly as it is in simulation. By using SC_CTHREAD for sequential logic and SC_METHOD for combinational logic, you guarantee your code remains safely within the synthesizable subset.

Comments and Corrections