cpptb

Write SystemVerilog testbenches as C++20 coroutines.

cpptb reads your RTL, generates a typed Dut you drive with ordinary C++, and runs your test inside the simulator. Signal reads and writes stay explicit, and every point where simulation time advances is a visible co_await:

Task<void> counter_sequence(Dut dut, TestContext& test) {
    dut.clk.set_now(0);
    test.start_clock(dut.clk, 10_ns);   // the testbench owns clock timing

    dut.rst_n.set(0);
    dut.enable.set(0);
    co_await clock_cycles(dut.clk, 2);

    dut.rst_n.set(1);
    dut.enable.set(1);

    co_await RisingEdge{dut.clk};
    co_await ReadOnly{};                // let the design settle, then check
    test.expect_eq("enabled count", dut.count.get(), 1u);
}

CPPTB_REGISTER_TEST(counter_sequence);

There is no manifest to write, no hand-authored DPI wrapper, and no Makefile to maintain. Point the cpptb command at your RTL and a testbench.cpp, and it derives the rest.

Start here

If you…

Read this

are new to cpptb

Getting started — install, run a real test, then make it fail

want the mental model

Core ideas — how a cpptb test is put together, in ten minutes

already write cocotb

Coming from cocotb — the trigger map, the write model, and three translation traps

want working code to copy

Examples — every one runnable, each with a pure-SystemVerilog twin

want the API at a glance

API reference — every common operation, one line each

are looking up an API

Library reference — signatures and semantics, one page per API family

need the exact option or key

cpptb command line and cpptb.toml — the command and configuration references

hit an error message

Troubleshooting — the diagnostics in one place, each with its fix

meet an unfamiliar term

Glossary — the vocabulary the rest of the docs uses

What you get

  • Typed access to the whole design. Ports, packed and fixed-point values, arrays, structs, enums, and the full internal hierarchy — inferred from RTL, with no probe list to maintain. Misspell a signal and the compiler says so.

  • Explicit, readable time. get() and set() never move the clock; only co_await does. Scheduling defines edge, delay, and ordering semantics precisely.

  • Real concurrency primitives. spawn, Join, First, events, bounded typed queues, locks, semaphores, timeouts, and test-owned process cleanup.

  • Verification components when you want them. The optional cpptb_vc layer adds transaction endpoints, scoreboards, APB components, sparse expected memory, and typed register models generated from SystemRDL or IP-XACT.

  • Randomization that replays. Deterministic per-process streams, constrained transactions, functional coverage, and replay from a recorded seed.

  • Performance you can check. Every authoring feature has an equivalent pure-SystemVerilog testbench, held to a hard ratio guard on every run. See Performance for the measurements and the benchmark contract.

Known limits

Stated here rather than discovered the hard way:

  • Verilator is the reference simulator. Two timing backends carry the full phase contract — verilator-direct for speed and vpi for portability — and are held to identical results on every run. Broader simulator support is active work.

  • A timing backend is always in play. timing_backend defaults to "verilator-direct" ("vpi" is the portable alternative) and supplies the phase waits ReadWrite{}, ReadOnly{}, and NextTimeStep{} — the hooks that let a testbench act at any point of a timestep. There is no supported build without one. See Timing backend support.

  • Four-state simulation is not available on the Verilator backend today. Four-state values explains what does and does not work.

Project status

cpptb is experimental and under active development. The roadmap tracks every milestone with its current status, scope, and design constraints; future directions collects unscheduled ideas.

The framework is validated against real, third-party verification environments, not only its own examples: Ibex’s core-level testlist runs on cpptb with the identical 912-of-944 outcome as the upstream UVM environment and 871,825 instructions co-simulated against Spike. See Ports of real testbenches.