Skip to content

Common SystemC Library

Mikhail Moiseev edited this page Jan 7, 2023 · 10 revisions

Common SystemC Library consists of types, modules and functions which could be used in designs and testbench code. All the components are synthesizable with Intel Compiler for SystemC.

Common SystemC Library has three parts:

  • Inter-process and inter-module communication channels
  • Integer types and common functions
  • Immediate and temporal assertions synthesizable into SVA

Inter-process and inter-module communication channels

Common SystemC Library contains nine main channels in the library.

image

Target and Initiator are intended to connect two SC modules with 1:1 connection. Multi-target and Multi-initiator modules provides 1:N connection to connect multiple SC modules. FIFO is intended to connect two processes in the same module or to serve as a buffer for one process. On-die port represents any external port to connect SC design to other IPs or fabric. Memory represents any kinds of on-chip SRAM, RF or ROM memory. Register is used to add state for METHOD process. The common use cases of the modules are given in the picture below.

image

The Single Source modules work in two modes: RTL and TLM. RTL mode intended for hardware synthesis. In RTL mode the modules provide cycle accurate simulation. TLM (Transaction Level Modelling) mode provide fast simulation, intended for virtual prototyping. In TLM mode the modules provide approximate time simulation, there is no clock. Simulation is request-driven, executed in ordered delta-cycles (DC).

Currently only FIFO channel in RTL mode is available under open source license.

Library defines

There are multiple options for clock/reset levels:

  • SCT_DEFAULT_TRAITS -- clock edge and reset level, one of six following options:
  • SCT_POSEDGE_NEGRESET -- positive clock edge, negative reset level
  • SCT_POSEDGE_POSRESET -- positive clock edge, positive reset level
  • SCT_NEGEDGE_NEGRESET -- negative clock edge, negative reset level
  • SCT_NEGEDGE_POSRESET -- negative clock edge, positive reset level
  • SCT_BOTHEDGE_NEGRESET -- both clock edges, negative reset level
  • SCT_BOTHEDGE_POSRESET -- both clock edges, positive reset level

Usually, positive clock edge and negative reset level are used. That is provided by define SCT_DEFAULT_TRAITS:

#ifndef SCT_DEFAULT_TRAITS
  #define SCT_DEFAULT_TRAITS SCT_POSEDGE_NEGRESET
#endif

If other clock edge/reset levels required, SCT_DEFAULT_TRAITS value should be provided as compile definition.

There is an CMakeLists.txt example where sct_def_traits target has definitions for negative clock edge and positive reset level:

add_executable(sct_def_traits sc_main.cpp)
target_compile_definitions(sct_def_traits PUBLIC -DSCT_DEFAULT_TRAITS=SCT_NEGEDGE_POSRESET)

Library interfaces

The interfaces contain non-blocking functions except b_put and b_get which are may-blocking.

Interface Functions Comment
sct_put_if bool ready() Return true if it is ready to put request
void reset_put() Reset this initiator/FIFO
void clear_put() Clear (remove) request put in this cycle
bool put(const T& data) Put request into initiator/FIFO if it is ready, return ready to request
bool put(const T& data, sc_uint<N> mask) Put request into initiator/FIFO if it is ready, mask used to enable/disable put or choose targets in multi-cast put, return ready to request
void b_put(const T& data) May-block put request, could be used in THREAD process only
void addTo(sc_sensitive& s) Add put related signals to process sensitivity
void addTo(sc_sensitive* s, sc_process_handle* p) Add put related signals to process sensitivity
sct_get_if bool request() Return true if it has request to get
void reset_get() Reset this target/FIFO
void clear_get() Clear (return back) request got in this cycle
T peek() Peek request, return current request data, if no request last data returned
T get() Get request and remove it from FIFO/target, return current request data, if no request last data returned
bool get(T& data, bool enable) Get request and remove it from FIFO/target if enable is true, return true if there is a request
T b_get() May-block get request, could be used in THREAD process only
void addTo(sc_sensitive& s) Add get related signals to process sensitivity
void addTo(sc_sensitive* s, sc_process_handle* p) Add get related signals to process sensitivity
void addPeekTo(sc_sensitive& s) Add peek related signal to process sensitivity
sct_fifo_if inherits sct_put_if<T> and sct_get_if<T>
unsigned size() FIFO size
unsigned elem_num() Number of elements in FIFO, value updated last clock edge for METHOD, last DC for THREAD
bool almost_full(const unsigned& N) Return true if FIFO has (LENGTH-N) elements or more, value updated last clock edge for METHOD, last DC for THREAD
void clk_nrst(sc_in<bool>& clk_in, sc_in<bool>& nrst_in) Bind clock and reset to FIFO
void addTo(sc_sensitive& s) Add put and get related signal to process sensitivity
void addToPut(sc_sensitive& s) Add put related signals to process sensitivity
void addToGet(sc_sensitive& s) Add get related signals to process sensitivity
sct_in_if const T& read() Read from signal/register
void addTo(sc_sensitive* s, sc_process_handle* p) Add signals to process sensitivity
sct_inout_if const T& read() Read from signal/register
void write(const T& val) Write to signal/register
void addTo(sc_sensitive* s, sc_process_handle* p) Add signals to process sensitivity

Functions addTo, addToPut, addToGet and addPeekTo are used to add the channel to process sensitivity list. For target and initiator instead addTo operator << can be used. For FIFO instead addToPut and addToGet operator << fifo.PUT and << fifo.GET can be used.

FIFO

The FIFO can be used for inter-process communication between processes in the same module and for storing requests inside one process.

image

The FIFO implements sct_fifo_if. FIFO has size template parameter which could be 0 or positive. Zero-size FIFO is a special case intended to communication with always ready target process (process which gets from the FIFO).

template<
    class T, 
    unsigned LENGTH,                    // Size (maximal number of elements)
    class TRAITS = SCT_DEFAULT_TRAITS,  // Clock edge and reset level traits
    bool TLM_MODE = SCT_DEFAULT_MODE>   // RTL (0)
>
class sct_fifo {};

The FIFO can have combinational or registered request (core_req and core_data) and response (core_ready) kind which specified in constructor parameters.

sct_fifo(const sc_module_name& name, 
         bool sync_valid = 0,       // Request path has synchronous register 
         bool sync_ready = 0,       // Response path has synchronous register  
         bool use_elem_num = 0,     // Element number/Almost full or empty used 
         bool init_buffer = 0)      // Initialize all buffer elements with zeros in reset
                                    // First element to get is always initialized to zero 

Minimal FIFO size required

Minimal FIFO size to provide full throughput depends on process types and request/response kind. In the table below minimal required FIFO sizes to provide full throughput are given.

Initiator process Target process sync_valid sync_ready Minimal FIFO size
method method 0 0 1
method method 0 1 1
method method 1 0 1
method method 1 1 2
method thread 0 0 1
method thread 0 1 2
method thread 1 0 2
method thread 1 1 3
thread method 0 0 1
thread method 0 1 2
thread method 1 0 2
thread method 1 1 3
thread thread 0 0 2
thread thread 0 1 3
thread thread 1 0 3
thread thread 1 1 4

Using FIFO for inter-process communication

FIFO should be used for processes communication instead of set of signals. FIFO has only one writer and one reader process, in comparison with sct_signal which could be read in multiple processes. For 1:N communication array or sc_vector of FIFOs should be used.

struct Top : public sc_module {
    sct_fifo<T, 2>      fifo{"fifo", 1};     // Pipelining register for request
    explicit Top(const sc_module_name& name) : sc_module(name) {
        fifo.clk_nrst(clk, nrst);
        SC_THREAD(producerProc); sensitive << fifo.PUT;   // Process puts to FIFO
        async_reset_signal_is(nrst, 0);
        SC_METHOD(consumerProc); sensitive << fifo.GET;   // Process gets from FIFO   
    } 
}

void producerProc() {
    fifo.reset_put();
    wait();
    while (true) {
       if (fifo.ready()) {           // If FIFO is ready put next value
          fifo.put(getSomeVal());
       }
       wait();
    }
}
void consumerProc() {
    fifo.reset_get();
    T val;
    if (fifo.get(val)) {
       doSomething(val);
    }
}

One process stores requests in FIFO

struct Top : public sc_module {
    sc_in<bool>         clk{"clk"};
    sc_in<bool>         nrst{"nrst"};
    sct_fifo<T, 5>      fifo{"fifo"};
    explicit Top(const sc_module_name& name) : sc_module(name) {
        fifo.clk_nrst(clk, nrst);
        SC_THREAD(storeProc); sensitive << fifo;  // Process puts and gets to FIFO
        async_reset_signal_is(nrst, 0);
    }
}

void storeProc() {
    fifo.reset();
    wait();
    while (true) {
       if (fifo.ready()) {
          fifo.put(getSomeValue());
       }
       wait(); 
       if (fifo.request()) {
          doSomething(fifo.get());
       }
    }
}

Integer types sct_int and sct_uint

The bit-accurate integer types which support any number of bits:

  • sct_int<N> is signed integer with N bits
  • sct_uint<N> is unsigned integer with N bits

SystemC contains sc_int/sc_uint integer types which are limited to 64 bit. Also SystemC contains sc_bigint/sc_biguint which can be more than 64bits , but are slow in simulation. Also all these integer types do not support 0 width. sct_int and sct_uint automatically substitute sc_int/sc_bigint(sc_uint/sc_biguint) depends on bit width. These types are implemented in sct_sel_types.h.

To initialize a variable of sct_uint type with all bits 0 or 1 there are special templates:

  • sct_zeros<N> is N-bit zeros literal of sct_uint type,
  • sct_ones<N> is N-bit ones literal of sct_uint type.
// Variable declaration with 0/1 initialization
sct_uint<12> a = sct_zeros<12>;
sct_uint<66> b = sct_ones<66>;
auto c  = sct_zeros<90>;
Clone this wiki locally