Chapter 13: Modeling Best Practices

Modeling Best Practices: API Docs and Doxygen

How to comment SystemC modules, sockets, registers, CCI parameters, callbacks, and examples so generated docs help real users.

Listen to this lessonAudiobook mode

How to Read This Lesson

Modeling Best Practices: API Docs and Doxygen

A SystemC Virtual Platform is a software product. Like any software library, if the APIs are not documented, they are unusable. In SystemC, the "APIs" are your module constructors, TLM sockets, CCI parameters, and memory-mapped register contracts.

The industry standard for C++ documentation is Doxygen. Doxygen comments should explain contracts and abstractions, not just repeat the C++ syntax. Furthermore, Accellera provides programmatic ways to expose this metadata directly into the simulation kernel.

Standard and source context

What to Document and Expose to the Kernel

When distributing a SystemC IP block, the following elements MUST be documented, and where possible, registered with the Accellera kernel APIs:

  • Module Abstraction Level: What does it model? (RTL, AT, LT/VP). What is intentionally left out?
  • TLM Sockets: Which protocols do they support? Do they support DMI? What is the expected bus width?
  • Registers: Base offsets, bitfields, reset values, and side-effects of reads/writes (e.g., "Reading this register clears the interrupt").
  • CCI Parameters (cci_param): Name, type, default value, and mutability rules. When instantiating cci_param, you should also use cci_param::set_description() so that tools querying the cci_broker_if can extract the Doxygen string at runtime via the JSON-based cci_value AST.
  • Report Message Types (msg_type): The string IDs used in SC_REPORT_ERROR. You should document these so integrators can configure the sc_report_handler to suppress or escalate specific warnings.

Doxygen Commenting Style

Use the standard Doxygen /** ... */ syntax.

Module and Abstraction Comment

/**
 * @class Uart
 * @brief Memory-mapped UART model for VP firmware bring-up.
 *
 * Models TX/RX FIFOs, status flags, and interrupt generation.
 * @note Bit-level serial waveform timing is intentionally abstracted.
 * Data is transferred instantaneously when the TX FIFO drains.
 */
class Uart : public sc_core::sc_module {

TLM Socket Comment

/**
 * @brief Target socket receiving memory-mapped register transactions.
 * 
 * Supports standard TLM-2.0 b_transport. DMI is NOT supported for 
 * memory-mapped peripheral registers. Expected payload width is 32 bits.
 */
tlm_utils::simple_target_socket<Uart> target_socket{"target_socket"};

CCI Parameter Comment (with Kernel Registration)

/**
 * @brief Approximate per-byte transmit delay.
 *
 * Mutable during simulation. Changing this affects future bytes only.
 * If set to SC_ZERO_TIME, the UART operates in zero-delay mode.
 */
cci::cci_param<sc_core::sc_time> tx_delay{"tx_delay", sc_core::sc_time(1, sc_core::SC_US)};
 
// Inside SC_CTOR, push the documentation to the CCI Broker:
// tx_delay.set_description("Approximate per-byte transmit delay. Mutable.");

Complete Example: A Fully Documented IP Block

Here is a complete sc_main demonstrates how a professionally documented SystemC IP block should look. It includes Doxygen groupings, parameter documentation, and programmatic registration.

#include <systemc>
#include <cci_configuration>
#include <iostream>
 
/**
 * @defgroup vp_timer Timer IP Block
 * @brief Abstract timer model for Loosely Timed (LT) Virtual Platforms.
 * @{
 */
 
/**
 * @class VpTimer
 * @brief A 32-bit countdown timer with interrupt generation.
 * 
 * This model uses SystemC SC_THREADs to abstract away clock cycles. 
 * It calculates the exact future time an interrupt should fire and waits 
 * for that duration, maximizing simulation speed.
 */
SC_MODULE(VpTimer) {
    /**
     * @brief Interrupt output signal.
     * Active HIGH. Level-triggered.
     */
    sc_core::sc_out<bool> irq_out{"irq_out"};
 
    /**
     * @brief Frequency of the timer.
     * Accessible via cci_broker_if.
     */
    cci::cci_param<int> frequency_hz{"frequency_hz", 1000000};
 
    /**
     * @name Register Offsets
     * Memory map offsets relative to the module base address.
     * @{
     */
    static constexpr uint32_t REG_CTRL  = 0x00; ///< Control register. Bit 0: Enable.
    static constexpr uint32_t REG_LIMIT = 0x04; ///< Value to countdown from.
    static constexpr uint32_t REG_ACK   = 0x08; ///< Write any value to clear IRQ.
    /** @} */
 
    /**
     * @brief Constructs the VpTimer.
     * @param name The SystemC hierarchical name.
     */
    SC_CTOR(VpTimer) {
        // Register the documentation string with the Accellera CCI broker
        frequency_hz.set_description("Clock frequency of the timer in Hz.");
        
        SC_THREAD(timer_process);
    }
 
private:
    void timer_process() {
        wait(10, sc_core::SC_NS); // Dummy logic for compilation
        irq_out.write(true);
    }
};
 
/** @} */ // End vp_timer group
 
int sc_main(int argc, char* argv[]) {
    // Instantiate the documented IP block
    sc_core::sc_signal<bool> irq_sig{"irq_sig"};
    VpTimer timer("my_timer");
    timer.irq_out(irq_sig);
 
    // Query the CCI Broker to demonstrate runtime metadata extraction
    cci::cci_broker_handle broker = cci::cci_get_broker();
    cci::cci_param_handle param = broker.get_param_handle("my_timer.frequency_hz");
    
    std::cout << "Runtime Param Description: " << param.get_description() << "\n";
 
    std::cout << "Starting Simulation of Documented IP...\n";
    sc_core::sc_start(1, sc_core::SC_US);
    
    return 0;
}

Explanation of the Execution

Runtime Param Description: Clock frequency of the timer in Hz.
Starting Simulation of Documented IP...

While running this code executes the simulation logic, running the doxygen tool against this source file will generate a professional HTML manual.

By combining static Doxygen comments with runtime cci_broker_if metadata (via set_description()), your models become fully introspectable. A firmware engineer can read the generated HTML to find that REG_ACK = 0x08, while an automated VP configuration tool can query the cci_broker_if at runtime to generate a GUI tooltip for the frequency_hz parameter.

Lesson self-check

Can you answer these clearly?

Keep moving when you can answer each question without looking back at the lesson.

Comments and Corrections