zxxrtl
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();
cxxrtl.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.
rst.next(true);
clk.next(false);
cxxrtl.step();
if (vcd) |*vcdh| vcdh.sample();
clk.next(true);
cxxrtl.step();
if (vcd) |*vcdh| vcdh.sample();
rst.next(false);
// Play out 10 cycles.
for (0..10) |_| {
clk.next(false);
cxxrtl.step();
if (vcd) |*vcdh| vcdh.sample();
clk.next(true);
cxxrtl.step();
if (vcd) |*vcdh| vcdh.sample();
}
if (vcd) |*vcdh| {
// Assume `alloc` exists.
const buffer = try vcdh.read(alloc);
defer alloc.free(buffer);
var file = try std.fs.cwd().createFile(vcd_out.?, .{});
defer file.close();
try file.writeAll(buffer);
}
Hopefully this is useful to someone else!