Chapter 0: Start Here

Official Resources Hub

Links to the IEEE Language Reference Manual, Accellera Forums, and official GitHub repositories.

How to Read This Lesson

Start here slowly. The goal is not to memorize APIs yet; it is to build the mental model you will reuse in every later SystemC design review.

Official SystemC Resources

While LearnSystemC aims to be your definitive guide and single source of truth, it is crucial to know where the official standards and community discussions reside.

SystemC is an open standard maintained by the Accellera Systems Initiative and standardized by the IEEE.

Source and LRM Trail

For this foundation lesson, keep three references close: Docs/LRMs/SystemC_LRM_1666-2023.pdf for portable semantics, .codex-src/systemc/src/sysc/kernel for kernel behavior, and .codex-src/systemc/src/sysc/datatypes for bit-accurate C++ types. When this lesson mentions a macro or type, the useful habit is to ask which C++ class the macro eventually creates.

1. The Language Reference Manual (LRM)

The final authority on how SystemC behaves is the IEEE 1666 Standard. If you ever want to know the exact mathematical or logical rules governing the simulator, read the LRM.

2. The Accellera Community Forums

The Accellera forums are the primary gathering place for SystemC engineers, tool vendors, and language architects. If you have a highly specific bug or compiler issue, search the forums first.

3. Official Source Code Repositories

The Accellera Proof-of-Concept (PoC) simulator and associated libraries are open source and hosted on GitHub. Browsing these repositories is highly recommended once you reach the advanced chapters of LearnSystemC.

Bookmark these links! They will be invaluable as you transition from a SystemC learner to a professional ESL architect.


Let's Connect This to the Standard and the Source

To effectively read and interpret the IEEE 1666-2023 Language Reference Manual and map it to the Accellera source code, you must understand how the standard is structured and where its rules are physically implemented.

Reading the IEEE 1666 LRM

The IEEE 1666 LRM is not a tutorial; it is a normative specification document. It is divided into several key sections. When you encounter a bug or need to verify a specific behavior, you should consult these chapters:

  1. Section 4 - Elaboration and Simulation Semantics: This chapter defines the simulation lifecycle. It dictates exactly what is allowed during elaboration (before simulation starts) and what the scheduling algorithm does during execution (the Delta Cycle, time progression, process awakening). The Accellera implementation for this resides heavily in sysc/kernel/sc_simcontext.cpp.
  2. Section 5 - Core Language (Modules, Processes, Events): This defines sc_module, sc_method_process, sc_thread_process, and sc_event. This is the structural backbone of SystemC.
  3. Section 6 - Communication (Ports, Interfaces, Channels): Defines how sc_port binds to sc_interface and how channels facilitate safe communication.
  4. Section 7 - Data Types: Defines the fixed-precision integer and logic types (sc_int, sc_logic).
  5. Section 8 - Predefined Channels: Specifies the exact behavior of sc_signal, sc_fifo, and sc_mutex, including their writer policies (SC_ONE_WRITER vs SC_MANY_WRITERS).
  6. Sections 10 through 16 - Transaction Level Modeling (TLM-2.0): Defines interoperability standards for memory-mapped buses, including the generic payload (tlm_generic_payload), blocking/non-blocking transport interfaces, and quantum keeping.

Navigating the Accellera SystemC Repository

The GitHub repository accellera-official/systemc mirrors the LRM architecture but uses deep C++ idioms to achieve the mandated behavior.

src/sysc/kernel/

This is the heart of the SystemC simulation engine.

  • sc_simcontext.cpp: Manages the global state. It contains the core simulation loop, handling the transition from evaluate phase to update phase (Delta Cycle).
  • sc_cor.h and sc_cor_qt.cpp / sc_cor_fiber.cpp: The underlying coroutine implementations. SystemC uses lightweight cooperative threads (coroutines) to implement SC_THREADs without the heavy OS overhead of std::thread. Depending on your OS, it uses QuickThreads, POSIX contexts, or Windows Fibers.
  • sc_runnable.cpp: The run queue. It holds the lists of runnable threads and methods for the current delta cycle.
  • sc_event.cpp: Implements the sc_event notification mechanisms, interacting directly with sc_simcontext's time wheel for delayed notifications.

src/sysc/communication/

This directory implements LRM Sections 6 and 8.

  • sc_port.cpp and sc_export.cpp: Implement the hierarchical binding mechanism. During elaboration, ports traverse their bindings to find the ultimate target interface implementation.
  • sc_signal.cpp: Implements the sc_signal<T> template. You can observe the update() phase logic here, which strictly enforces the read-only evaluate / deferred update semantics mandated by the LRM to prevent race conditions.
  • sc_fifo.cpp: Implements bounded, process-safe queues.

src/sysc/datatypes/

This directory implements LRM Section 7.

  • int/: Implements sc_int and sc_uint up to 64 bits.
  • bit/: Implements sc_logic and sc_lv (logic vectors).
  • fx/: Implements fixed-point data types (sc_fixed, sc_ufixed). These are crucial for DSP algorithm modeling and HW synthesis but are incredibly heavy on compile times due to massive template usage.

src/tlm_core/

This directory implements the TLM-2.0 standard (LRM Sections 10-16).

  • tlm_1/: Legacy TLM-1.0 interfaces (rarely used in modern code).
  • tlm_2/tlm_generic_payload/: Defines tlm_generic_payload, the universal bus transaction object.
  • tlm_2/tlm_sockets/: Defines the initiator and target sockets (tlm_initiator_socket, tlm_target_socket) which combine an sc_port and sc_export to enable bidirectional function calls.
  • tlm_2/tlm_quantum/: Implements the tlm_global_quantum class, which allows processes to decouple from the SystemC scheduler to maximize simulation speed (Temporal Decoupling).

Translating LRM Constraints to Source Code Limits

When the LRM states "An sc_signal shall only be updated by a single process unless the SC_MANY_WRITERS policy is specified," the Accellera source code enforces this by storing the ID of the process that first writes to the signal. If a different process ID attempts a write in the same delta cycle (or across the simulation if strictly enforced), the sc_signal throws a fatal sc_report exception, abruptly terminating the simulation. Understanding where these checks live (e.g., sc_writer_policy.h) allows you to debug complex architectural violations efficiently.

Standard and Source Deep Dive: Port Binding

Port binding is the topological glue of a SystemC model. The IEEE 1666-2023 LRM Sections 4.2.1 (Elaboration) and Section 6.11-6.13 (Ports, Exports, Interfaces) rigidly define how structural connections are made and verified.

Inside the Accellera Source: sc_port_b and sc_port_registry

In src/sysc/communication/sc_port.h/cpp, all specialized sc_port<IF> classes derive from a non-template base class sc_port_b. When you declare sc_port<BusIf> bus{"bus"};, the constructor ultimately calls sc_simcontext::get_port_registry()->insert(this).

The sc_port_registry (located in src/sysc/kernel/sc_simcontext.cpp) is the global list of every port in the simulation.

When you write cpu.bus.bind(subsystem.target); in your C++ code, you are invoking the bind() method on sc_port. However, this does not immediately resolve the C++ pointer! Instead, the port simply stores a generic pointer to the bound object in an internal array (because a port can be bound to multiple channels if the port's N parameter is > 1).

The Elaboration Phase: complete_binding()

The real magic happens when sc_start() is called. Before simulation begins, sc_start() invokes sc_simcontext::elaborate(), which ultimately calls sc_port_registry::complete_binding().

If you trace sysc/kernel/sc_simcontext.cpp, you will see complete_binding() iterate over every single port in the design. For each port:

  1. It traverses the binding tree. If Port A is bound to Port B, and Port B is bound to Channel C, it recursively walks from A -> B -> C to find the actual sc_interface implementation.
  2. Type Checking: It uses C++ RTTI (dynamic_cast) to verify that the target object actually implements the interface required by the port.
    // Abstract representation of the kernel's check:
    sc_interface* target_if = dynamic_cast<sc_interface*>(bound_object);
    if (!target_if) { SC_REPORT_ERROR("Port binding failed: interface mismatch"); }
  3. It resolves the final interface pointer and stores it directly inside the port's m_interface pointer array.

Zero-Overhead Simulation Dispatch

Why delay pointer resolution until complete_binding()? Because once elaboration finishes, the port has an absolute, direct C++ pointer to the implementing channel.

In src/sysc/communication/sc_port.h, the overloaded operator-> is extraordinarily simple:

template <class IF>
inline IF* sc_port<IF>::operator -> () {
    return m_interface;
}

During simulation, when a thread executes bus->write(0x10, data);, there are no map lookups, no string comparisons, and no routing tables. It is exactly equivalent to a direct C++ virtual function call on the channel object.

Comments and Corrections