Skip to content

SystemC supported

Mikhail Moiseev edited this page Jun 4, 2021 · 39 revisions

This page describes how various features of SystemC and C++ are supported by ICSC tool.

Terminology

  • Module -- SystemC module, a C++ class/structure inherits sc_module
  • Modular interface -- SystemC module which inherits sc_interface
  • Record -- C++ class/structure which is not module nor modular interface
  • Signal -- SystemC sc_signal
  • Port -- SystemC sc_in and sc_out
  • Port interface -- sc_port<IF> connected to module/modular interface which implements IF
  • SC vector -- SystemC sc_vector<T>, container contains objects of type T.

Elaboration stage requirements (Module constructors)

  1. Dynamic allocation requirements. Allocation using new is supported for sc_objects (ports, signals, modules). For all other cases new is not supported, use use sc_new for single object and sc_new_array for array. Allocation with sc_new and sc_new_array supports all usage cases of built-in new.
  2. Unique owning pointer for heap-allocated objects. Two pointers to same object are not supported and leads to incorrect code generated.
  3. Non-rectangular data structures are not supported, with exception for arrays of modules. Rectangular data structures means all the pointers in array points to sub-array of the same size.
  4. All ports must be connected to signals in the design or its testbench. If a port not connected to a signal in the design (connected in testbench), such a port is promoted to design top module port which has the correspondent name. In case of name collision, numerical suffix is used (suffix starts with 0).

Process code requirements (Methods and threads)

  1. Module is C++ class or structure inherits sc_module. Module can have data members, signal/port members, and module/modular interface/record members. Module can have method and clocked thread processes. Module can have process functions as well as other member functions.
  2. Modular interface is module which inherits sc_interface. Modular interface has all module features, but in generated SV it is flattened into its parent module. That gives more flexibility in using modular interface, including access its members and call member functions from parent module and peer modular interfaces.
  3. Any module/modular interface must have at least one constructor. Module/modular interface constructor must have sc_module_name parameter which can be passed to sc_module inheritor or just not used. Module/modular interface constructor can contains arbitrary C++ code.
  4. Record is C++ class/structure which is not SystemC module or modular interface. Record can be member of module/modular interface as well as local variables in process functions. Records intended to represent set of plain data fields only.
  5. Records are supported with limitations. Record can have member functions and data type members. Record cannot have another record members, signal/port or module/modular interface members. Record cannot have any pointer or array members. Record cannot have any base classes.
  6. Record can have constructor, field in-place initialization and initializer list. Record constructor cannot have function calls.
  7. There is one top module in design. Top module can contains module instances as well as other fields. All instances are module fields, no global/namespace variables supported. Global/namespace constants supported.
  8. Module/modular interface/record non-static data members and static constant data members supported. No static non-constant data members allowed.
  9. Module/modular interface/record static and non-static functions supported. Global/namespace functions supported. Recursive functions not supported.
  10. Module/modular interface can contains thread and method processes. There are thread processes created with SC_THREAD and SC_CTHREAD to implement sequential logic. SC_THREAD must be sensitive to clock edge and optionally to reset(s). SC_CTHREAD must have clock edge as its second parameter.
  11. A thread process supports multiple asynchronous and synchronous resets as well as no reset. async_reset_signal_is and reset_signal_is can be used with any combination and any active level (1 or 0) .
  12. There is method process created with SC_METHOD to implement combinational logic. SC_METHOD can be sensitive to reset signal, but not to clock (In the future, this feature will be supported, but now SC_METHOD cannot be used for sequential logic).
  13. SC_METHOD must have all the signals/ports which are read in its body in the sensitivity list. Method process without sensitivity list (empty sensitivity) is a special case. Such a method typically contains signal/port initialization with constant value (1).
  14. SC_METHOD cannot read signal/port which is modified in this process. Violation of this rule leads to incorrect always_comb in generated SV.
  15. C++ types bool, char, short, int, long int and long long int and their unsigned versions supported. SystemC data types sc_int, sc_uint, sc_bigint, sc_biguint supported.
  16. Reference/constant reference types supported. Reference can be initialized with explicit object of the corresponding type, no conditional initialization supported. Constant reference can be initialized with variable, literal or constant expression. There are limitations on such constant expression: no array access, no function call, no ternary operator.
  17. C++ types can be used together with SC types. Signed and unsigned types should never be mixed, as that can lead to unexpected result for operation with negative values (2).
  18. Result of operation in generated SV is the same as in correspondent operation in SC code. That means simulation results are equivalent. Only one exception is bitwise not ~ for C++ boolean type. As C++ boolean in promoted to int, in bitwise operator all bits are considered that leads to different semantic than in generated SV. Therefore bitwise not ~ is prohibited for boolean argument, use logical not ! instead (3).
  19. There are several operations where SystemC and SV have different rules for left operand cast: >>, /, %. These operations cannot have as left operand expression with sc_bigint/sc_biguint variables or signals. Single sc_bigint/sc_biguint variable or signal/port object can be used there (4).
  20. Pointers are supported. Pointers are normally initialized at elaboration phase and de-referenced in process body. Pointer dereference * and -> supported. Pointer comparison supported. Obtaining object address with & is not supported. Operator new/new[] and operator delete/delete[] not supported.
  21. Local pointers to non-port/non-channel object supported. Local pointer to port/signal not supported. Pointer assignment is supported at local pointer variable declaration only. Local pointer without declaration allowed, but makes no sense.
  22. Pointer arithmetic not supported and general pointer assignment not supported (6). Pointer can be assigned to boolean variable, as well as, used in comparisons and conditions.
  23. Function can have parameters and returned value. Function can have local variable of non-signal/non-port type. Local variables can be non-static or constant static. No static non-constant local variables allowed.
  24. Function parameters can be passed by value, by reference reference, and by pointer, including pointer to channel. Constant reference parameter argument can be literal of the corresponding type. Function can return result by value only. Return by reference or by pointer not supported. Function with return type void can use return statement without argument.
  25. Record can be passed to function by value as well as by reference/constant reference, such record must have trivial copy constructor. Record can be returned from function by value, such record must have trivial copy constructor. Module or modular interface cannot be passed to or returned from function.
  26. Function with multiple return supported. Function return statement(s) is replaced with function result to variable assignment in SV code. That leads to a function must have no code after return. In particular, return statement in loop is not supported. Function with return type void can have return statement(s) without argument. For return statement without argument no code is generated.
  27. Module member function can access this module fields/functions and child modular interface instance(s) fields/functions. Access to child modular interface instance members allowed through a port interface (sc_port<IF>) or a pointer to the modular interface. The accessed modular interface is flatten in module/modular interface with does access to it.
  28. Virtual functions supported. Function overload and hide function in child class supported.
  29. Conditions of if, ?, for, while, do...while should be expression without side effects. Complex conditions with ||, &&, ! and brackets supported. If left part of logical expressions with || and && evaluated as constant, right part code is not generated. That allows to check pointer is not null and do the pointer de-reference it in the condition expression. if and ? conditions, including complex conditions, can contain function call without side effects and without wait() (5). for, while, do...while conditions cannot have any function call.
  30. switch statement can have one or more cases including optional default case. Each case must have one and only one final break as the last statement of the case. default case also must contain final break. Another option is empty case or empty default case. For empty case the next non-empty case (possibly default case) code is copied in the generated SV.
  31. switch case code can contain if/loop statements as well as inner switch statements. switch case code can contain function calls. switch statement in called function can contains return statements in the end of all cases. For such switch cases final break statement not allowed, no mix of return and break supported.
  32. for loop can have only one counter variable with optional initialization, condition and increment sections. The variable can be declared in the loop initialization section or before the loop.
    • Initialization section can have simple variable initialization or assignment only, cannot have function call.
    • Condition section can have one comparison operator for the loop variable, cannot have function call.
    • Increment section can have increment or decrement of the loop variable, cannot have function call.
  33. An empty loop is removed by default. There is no issue with loop condition as it cannot contain any side effects. There can be issue with the removed loop counter, if it declared outside and used after the loop. To avoid empty loops/if/switch removing NO_REMOVE_EXTRA_CODE option should be used (see details at Tool options).
  34. Arrays are supported as module members and function local variables. Multidimensional arrays supported. Array of pointers supported. Array of signals/ports supported. Array of signal/port pointers supported.
  35. Array in-place initialization supported. Array initialization by zeros with empty brackets {} supported (7).
  36. Array of modules supported. Array of pointers to module/modular interface supported. In array of module/modular interface base class pointers all elements must be the same class (virtual functions analyzed for the first element only). Array of modular interfaces not supported yet (will be supported in 1.4). Array of port interfaces (sc_port<IF>) not supported.
  37. Array of records supported. Array of pointers to record supported. In array of base class pointers all elements must be the same class (virtual functions analyzed for the first element only). Record which is element of an array must have empty constructor and no member initialization.
  38. Array of any pointers must be homogeneous, all elements created with operator new, but not pointers to existing objects. That includes array of record/modular interface pointers. Record/modular interface in array cannot have members of any pointer type, port interface (sc_port<IF>), array of pointers or port interfaces, array of record/modular interface. Such record/modular interface can have non-pointer members as well as array of data and ports/signals.
  39. Array of record cannot be accessed at index which is the same array element, for example: a[a[i].m].m; -– not supported, a[b[i].n].m –- supported.
  40. Array and array of pointers, including array of signals/ports, can be passed into function. Multidimensional array also can be passed into function.
  41. SC vector (sc_vector) supported for signals/ports and modules. SC vector is not supported for modular interface yet. Two-dimensional vector (vector of vectors) also supported. SC vector instances can be instantiated in modules and modular interfaces, including array of modular interfaces. SC vector cannot be passed to or returned from function.
  42. Uninitialized local variable of SystemC data type (sc_uint, sc_biguint, ...) has got zero value in generated code as it got in the default constructor.
  43. Non-constant module fields cannot be initialized in constructor or in place. After elaboration phase non-constant fields have unknown value. They need to be initialized in reset section of a process.
  44. Constant fields can be initialized in module constructor initialization list and body, they are generated as localparam in SV. Constant of any integral type initialized at elaboration phase should be 64 bit or less (stored in int64 field). Constant more than 64 bit has unknown value after elaboration.
  45. Type cast in C style ((T)x), functional style (T(x)), and static cast supported for right side of assignment statement and function arguments. Type cast for left side of assignment is ignored. Const type cast ignored. Reinterpret and dynamic type cast not supported (dynamic type cast may be supported in future). Type cast to base class supported for function call (T::f()) and member access (T::m).
  46. Dead code is eliminated from analysis and SV generation. Dead code is determined by evaluating condition based on compilation time constants (static constant and constexpr supported). Dead code determination does not use loop counters and other variables even they can be evaluated as constant. Accessing not allocated object and arrays in dead code and only in dead code allowed. Accessing not allocated objects under non dead code or dead code which cannot be evaluated in described manner is prohibited and leads to synthesis error.

(1) In SC_METHOD without sensitivity list local variable can be declared and used to store intermediate results. Module variable initialization can be done in such method process, but not recommended as can lead to concurrent assignment to the variable. Ternary operator (?) with arbitrary condition could be used. if statement with statically evaluated condition could be used. Read port/signal in such method is supported, but not recommended as can lead to different behavior in SystemVerilog vs SystemC (in SystemC process not activated if signal is changed). Loops and other control flow statements cannot be used here. In the SV one or more assign statements are generated for such method.

(2) For operation with signed and unsigned arguments of sc_int/sc_uint types SystemC simulation differs from generated SV simulation. The tool constant evaluation complies to SystemC semantic, so it differs from SV simulation results (see example below).

(3) C++ boolean in promoted to int in bitwise operator, so generated SV differs from its source:

bool a = true;
bool b = ~a;    // b == true
assert (b);
logic a = 1;
logic b = ~a;   // b == 0  
assert (b == 0);

(4) Example for >> (the same true for \ and %):

sc_uint<32> a; 
sc_biguint<128> b; 
auto c = (a + 1) >> 8;  // OK: type of variable a is sc_uint
auto d = (b + 1) >> 8;  // Error: type of variable b is sc_biguint
auto e = b >> 8;        // OK: single sc_biguint variable used 

(5) Function call with side effect is not supported in complex conditions with || and && as function body is inlined and returned value stored in temporary variable.

(6) Pointer assignment in general case can lead to different pointer values in branches/iterations possible. Pointer is replaced in generated code by its value variable, so it should have the same value at any Phi function. As this constraint is difficult to check, general assignment is prohibited.

(7) Example for array initialization:

int a[3][2] = {};                 // Initialization by zeros
sc_uint<4> b[3][2];               // Initialization by zeros for SC integer, the same as using {}
int c[3][2] = {1,2};              // Partial initialization
int d[3][2] = {1,2,3,4,5,6};      // All elements initialization
int e[3][2] = {{1,2,3},{4,5,6}};  // All elements initialization

Supported C++ and SystemC types

Supported data types conversion into SV. If type width in the current platform differs from specified by SC synthesizable standard, warning is reported. long and unsigned long translated into 32bit or 64bit SV type depends on current platform.

C++/SC type SV type SC synthesizable standard
sc_int<N>, sc_bigint<N> logic signed [N] Nbit
sc_uint<N>, sc_biguint<N> logic [N] Nbit
bool logic 1bit
char, signed char logic signed [8] 8bit
unsigned char logic [8] 8bit
short logic signed [16] 16bit
unsigned short logic [16] 16bit
int integer 32bit
unsigned int integer unsigned 32bit
long logic signed [32] / logic signed [64] 32/64bit depends on platform
unsigned long logic [32] / logic [64] 32/64bit depends on platform
long long logic signed [64] 64bit
unsigned long long logic [64] 64bit
int8_t, int16_t, int32_t, int64_t logic signed [8] / logic signed [16] / logic signed [32] / logic signed [64] 8bit / 16bit/ 32bit/ 64bit
uint8_t, uint16_t, uint32_t, uint64_t logic [8] / logic [16] / logic [32] / logic [64] 8bit / 16bit/ 32bit/ 64bit
__int128_t logic signed [128]
__uint128_t logic [128]

Unsupported C++ and SystemC types

  • sc_event
  • sc_fifo, sc_mutex, sc_semaphore, sc_buffer
  • sc_signal_resolved, sc_signal_rv
  • sc_inout
  • sc_logic , sc_lv , sc_bv, sc_bit
  • fixed-point types: sc_signed, sc_unsigned, sc_fix, sc_ufix, sc_fixed, sc_ufixed
  • floating-point types: float, double
  • sc_export

Special cases and examples

Mixing signed and unsigned types

Never mix signed and unsigned types as arguments of one operations. There are different rules to promote C++/SystemC types to signed or unsigned, which can give unexpected result. Unsigned types should be used for non-negative values only, and in expression which always evaluated as non-negative.

Unique owning pointer for heap-allocated objects

SC_MODULE(top) {
sc_signal <bool> * a_ptr;
sc_signal <bool> * b_ptr0, b_ptr1;
SC_CTOR(top) {
    a_ptr = new sc_signal<bool>("a");    // OK
    b_ptr0 = new sc_signal<bool>("b");   // OK
    b_ptr1 = b_ptr0;                     // Error, second pointer for same object
}};

sc_new and sc_new_array declarations

template<class T, class... Args>
T* sc_new(Args&&... args);
 
template<class T>
T* sc_new_array(size_t array_size);

Using sc_new and sc_new_array examples:

struct MyType {
    MyType(int);
}
SC_MODULE(top) {
   sc_signal<bool>* a_ptr = sc_signal<bool>("a");      // OK, signal is sc_object
   MyType* my_ptr = new MyType(42);                    // ERROR, new for non-sc_object not supported
   MyType *my_ptr = sc_new<MyType>(42);                // OK, using sc_new
   sc_in<int>** ports = new sc_in<int>* [10];          // ERROR, pointer is not sc_object
   sc_in<int>** ports = sc_new_array<sc_in<int>*>(10); // OK, using sc_new_array
   sc_signal<int>* signals = new sc_signal <int>[10];  // OK, array of sc_objects 
};
Clone this wiki locally