Modeling Best Practices: Abstraction
How to decide what to model, what to approximate, what to document, and how to keep SystemC examples production-like.
Modeling Best Practices: Abstraction
The most important SystemC skill is not knowing every API. It is choosing the right abstraction level. The IEEE 1666 LRM provides mechanisms ranging from bit-accurate, delta-cycle-level RTL modeling all the way up to Loosely Timed (LT) Transaction Level Modeling (TLM-2.0).
A good architect uses the simplest, fastest abstraction that still answers the system's design questions.
The Spectrum of Abstraction
-
Cycle-Accurate (RTL Level): Uses
sc_signal,sc_logic, clocks, and delta cycles.- Pros: Matches actual hardware perfectly.
- Cons: Extremely slow simulation (KHz range).
- When to use: Hardware validation, HLS (High-Level Synthesis) generation.
-
Approximately Timed (AT) TLM: Uses TLM-2.0 Non-Blocking Transport (
nb_transport), modeling multiple protocol phases (Request, End-Request, Response) with annotated delays.- Pros: Highly accurate bus contention and performance profiling.
- Cons: Harder to write, moderate simulation speed.
- When to use: Interconnect performance analysis, cache coherency studies.
-
Loosely Timed (LT) TLM (Virtual Platforms): Uses TLM-2.0 Blocking Transport (
b_transport), temporal decoupling, and quantum keepers.- Pros: Blistering fast simulation speed (100s of MHz). Capable of booting Linux in seconds.
- Cons: Timing is approximate; cannot find race conditions on the bus.
- When to use: Firmware development, early software bring-up, OS porting.
Avoid Accidental RTL
If you are building a Virtual Platform (LT), avoid mixing RTL-style modeling inside it. If you model every clock edge of a UART transmitter using sc_signal<bool> and wait(), your entire platform's speed will be bottlenecked by that one UART.
Instead, model the UART at a high level: when software writes a byte, wait a bulk block of time (wait(byte_delay)), and then raise an interrupt.
Complete Example: Abstracting a Timer
This complete sc_main demonstrates the difference between an RTL-style timer (bad for VPs) and an abstract TLM-style timer (good for VPs).
#include <systemc>
#include <iostream>
// ---------------------------------------------------------
// BAD for Virtual Platforms: Cycle-Accurate Timer (RTL Style)
// ---------------------------------------------------------
SC_MODULE(RtlTimer) {
sc_core::sc_in<bool> clk{"clk"};
sc_core::sc_out<bool> irq{"irq"};
int counter = 0;
int limit = 1000;
SC_CTOR(RtlTimer) {
SC_METHOD(tick);
sensitive << clk.pos();
}
void tick() {
// Wakes up on EVERY SINGLE CLOCK CYCLE! Very slow simulation.
counter++;
if (counter >= limit) {
irq.write(true);
counter = 0;
} else {
irq.write(false);
}
}
};
// ---------------------------------------------------------
// GOOD for Virtual Platforms: Abstract Timer (TLM Style)
// ---------------------------------------------------------
SC_MODULE(AbstractTimer) {
sc_core::sc_event irq_event;
sc_core::sc_time clock_period;
int limit = 1000;
SC_CTOR(AbstractTimer) : clock_period(10, sc_core::SC_NS) {
SC_THREAD(timer_process);
}
void timer_process() {
while (true) {
// Wakes up ONLY when the interrupt is actually due!
// Skips 1000 clock cycles instantly. High simulation speed.
sc_core::sc_time wait_time = clock_period * limit;
wait(wait_time);
std::cout << "@ " << sc_core::sc_time_stamp()
<< " [AbstractTimer] Interrupt Fired!\n";
irq_event.notify(sc_core::SC_ZERO_TIME);
}
}
};
int sc_main(int argc, char* argv[]) {
// We only simulate the AbstractTimer for this demonstration.
AbstractTimer t_abs("abstract_timer");
std::cout << "Starting Virtual Platform simulation...\n";
sc_core::sc_start(25, sc_core::SC_US);
return 0;
}Explanation of the Execution
Starting Virtual Platform simulation...
@ 10 us [AbstractTimer] Interrupt Fired!
@ 20 us [AbstractTimer] Interrupt Fired!
The RtlTimer would require the SystemC kernel to evaluate the tick() method 2,500 times to simulate 25 microseconds (assuming a 10ns clock).
The AbstractTimer requires the SystemC kernel to evaluate timer_process() exactly 2 times. By abstracting away the clock signal and calculating the bulk time jump, simulation performance increases by orders of magnitude, making firmware development practical.
Comments and Corrections