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