abstraction

SystemC vs Bare C++

2026 · 8 min read

We build models in both. And the most common wrong decision we see isn't a bad model — it's a model in the wrong vehicle: a single block that didn't need a simulation kernel, or a platform that absolutely did. Here's the decision, stated plainly.

What SystemC actually is

SystemC is not a modeling language. It's a discrete-event simulation kernel wearing a C++ class library. What you buy with it: a scheduler that decides when things happen, a time model that everyone in the platform agrees on, and a set of interoperability standards — TLM 2.0 sockets, phases, and payload conventions — that let models written by different people, in different years, talk to each other.

What you pay for it: the kernel's scheduling overhead on every event, a build and debug experience that's C++ with extra ceremony, and a framework whose idioms every engineer on the project now has to know. None of that is free. All of it is worth paying — sometimes.

The case for bare C++

A single block model — a peripheral, a controller, a datapath — that exists to answer questions about itself doesn't need any of that. What does the block do given this stimulus? What are its corner cases? Does it match the spec? A plain C++ program with a test harness answers those questions faster to build, faster to run, and easier to debug than the same logic inside an SC_MODULE. No scheduler, no sockets, no phases — just the behavior and a main().

This is also the honest vehicle for cycle-approximate work. If the model's job is to be correlated against RTL — same stimulus, same trace, diff the outputs — then what matters is the trace, not the framework. A bare C++ model with a clean cycle log is every bit as correlatable as a SystemC one, and it's usually faster, because nothing is paying kernel tax on the way to the observation point.

The case for SystemC

The moment the question stops being about one block, the calculus flips. SystemC earns its keep when:

The failure modes, both directions

We see both mistakes, roughly equally:

SystemC where it doesn't belong. A single block wrapped in SC_MODULEs and sockets because that's what the last project used. The model takes longer to build, runs slower, and — the quiet cost — the framework's idioms start dictating the design. The behavior contorts to fit the vehicle. When a reviewer asks "why is this a socket?" the honest answer is "it didn't need to be."

Bare C++ where it doesn't belong. A platform built as a constellation of ad hoc models, each with its own private timing convention, stitched together by whoever needed the integration that week. It works — until it doesn't, and then the debugging question "whose time model is wrong" has no answer, because there were four. The platform that should have been SystemC from week one gets rebuilt as SystemC in a panic later.

The decision, compressed

Strip the ceremony and it's one question: does the model need to interoperate?

And one meta-rule, because it's the one that gets skipped: whichever vehicle you pick, the correlation contract — same stimulus, same observation points, same tolerance — is identical. The framework is a delivery mechanism for behavior. The behavior is the deliverable. Teams that keep that ordering straight pick the vehicle correctly almost by accident; teams that don't end up with a beautiful platform whose models nobody trusts.

← All notes