I’ve been getting back into using CXXRTL and Zig together, so I’ve extracted and rendered somewhat reusable the bindings I made to use them together!

zxxrtl uses CXXRTL’s C API to provide a somewhat idiomatic way to access, manipulate, and respond to events happening in the design. Its README covers the setup — it’s a bit involved as it’s necessarily something of a build system, but once you’re done it’s good to go and flexible enough to be instrumented from a higher build system.

I’m going to paste the example usage here; this doesn’t use the Sample API for edge detection, and just drives the simulation while optionally recording VCD:

const Cxxrtl = @import("zxxrtl");

// Initialise the design.
const cxxrtl = Cxxrtl.init();

// Optionally start recording VCD. Assume `vcd_out` is `?[]const u8` representing an
// optional output filename.
var vcd: ?Cxxrtl.Vcd = null;
if (vcd_out != null) vcd = Cxxrtl.Vcd.init(cxxrtl);

defer {
    if (vcd) |*vcdh| vcdh.deinit();

// Get handles to the clock and reset lines.
const clk = cxxrtl.get(bool, "clk");
const rst = cxxrtl.get(bool, "rst");  // These are of type `Cxxrtl.Object(bool)`.

// Reset for a tick.;;
if (vcd) |*vcdh| vcdh.sample();;
if (vcd) |*vcdh| vcdh.sample();;

// Play out 10 cycles.
for (0..10) |_| {;
    if (vcd) |*vcdh| vcdh.sample();;
    if (vcd) |*vcdh| vcdh.sample();

if (vcd) |*vcdh| {
    // Assume `alloc` exists.
    const buffer = try;

    var file = try std.fs.cwd().createFile(vcd_out.?, .{});
    defer file.close();

    try file.writeAll(buffer);

Hopefully this is useful to someone else!