Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/kivikakk/zxxrtl
Idiomatic CXXRTL bindings for Zig
https://github.com/kivikakk/zxxrtl
cxxrtl yosys zig
Last synced: 4 days ago
JSON representation
Idiomatic CXXRTL bindings for Zig
- Host: GitHub
- URL: https://github.com/kivikakk/zxxrtl
- Owner: kivikakk
- License: bsd-2-clause
- Created: 2024-06-16T13:13:19.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2024-10-12T06:24:35.000Z (3 months ago)
- Last Synced: 2025-01-11T22:56:51.107Z (11 days ago)
- Topics: cxxrtl, yosys, zig
- Language: Zig
- Homepage:
- Size: 9.77 KB
- Stars: 4
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# zxxrtl
[CXXRTL] bindings for Zig.
[CXXRTL]: https://yosyshq.readthedocs.io/projects/yosys/en/latest/cmd/write_cxxrtl.html
## Example
[ili9341spi] uses this in combination with [Niar] to build a CXXRTL simulation
with [Amaranth] and Zig.[ili9341spi]: https://github.com/kivikakk/ili9341spi
[Niar]: https://github.com/kivikakk/niar
[Amaranth]: https://amaranth-lang.org## Setup
> [!NOTE]
> This guide assumes you're driving the build from outside, and use Zig's build
> system just to build the Zig parts and link the final object. This gives you a
> lot of flexibility, but if you don't need it, you can simplify by bringing the
> CXXRTL object file building into your `build.zig` too. Refer to [zxxrtl's
> `build.zig`] for guidance.[zxxrtl's `build.zig`]: https://github.com/kivikakk/zxxrtl/blob/main/build.zig
Add zxxrtl to your `build.zig.zon`:
```console
zig fetch --save https://github.com/kivikakk/zxxrtl/archive/.tar.gz
```Now let's add the import to your `build.zig`. We'll take the CXXRTL object file
list as an option in the `build()` function:```zig
const cxxrtl_o_paths = b.option([][]const u8, "cxxrtl_o_path", "path to .o file to link against")
orelse &[_][]const u8{"../build/cxxrtl/ili9341spi.o"};
```We supply a default value for the object file paths --- it should match your
development environment. This is to ensure ZLS still works.Then add the dependency, and import the module into your executable:
```zig
const zxxrtl_mod = b.dependency("zxxrtl", .{
.target = target,
.optimize = optimize,
}).module("zxxrtl");
exe.root_module.addImport("zxxrtl", zxxrtl_mod);
```The last step is to link against the CXXRTL object files:
```zig
for (cxxrtl_o_paths) |cxxrtl_o_path| {
exe.addObjectFile(b.path(cxxrtl_o_path));
}
```### Specify Yosys' data dir
If you want to be able to specify the Yosys data dir from the `zig build` line,
you can specify it when adding the zxxrtl dependency. Here we add an option,
with a default fallback to actually calling `yosys-config --datdir` for ZLS or
the lazy:```zig
const yosys_data_dir = b.option([]const u8, "yosys_data_dir", "yosys data dir (per yosys-config --datdir)")
orelse @import("zxxrtl").guessYosysDataDir(b);
```Now adapt the `b.dependency()` call:
```zig
const zxxrtl_mod = b.dependency("zxxrtl", .{
.target = target,
.optimize = optimize,
.yosys_data_dir = yosys_data_dir,
}).module("zxxrtl");
```## Usage
```zig
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);
}
````Cxxrtl.Object(T)` is the basic interface to CXXRTL objects. It exposes two
methods: `curr(Self) T`, and `next(Self, T) void`, which get the current value,
and set the next value respectively.There's also a helper, `Cxxrtl.Sample(T)`, which is used for change detection in
driver loops: you call its `tick(Self)` on every trigger edge, and then can
query its `prev` and `curr` values, and if it's `stable(Self)`. If `T == bool`,
you can also ask whether it's `falling(Self)`, `rising(Self)`,
`stable_low(Self)` or `stable_high(Self)`.The following example is adapted from an SPI peripheral blackbox. Each byte of
payload from the design is specified as data or command depending on the `dc`
line during the last bit. The module returns events to the caller on each tick.```zig
const std = @import("std");
const Cxxrtl = @import("zxxrtl");const SpiConnector = @This();
clk: Cxxrtl.Sample(bool),
res: Cxxrtl.Sample(bool),
dc: Cxxrtl.Sample(bool),
copi: Cxxrtl.Sample(bool),sr: u8 = 0,
index: u8 = 0,const Tick = union(enum) {
Nop,
Command: u8,
Data: u8,
};pub fn init(cxxrtl: Cxxrtl) SpiConnector {
const clk = Cxxrtl.Sample(bool).init(cxxrtl, "spi_clk", false);
const res = Cxxrtl.Sample(bool).init(cxxrtl, "spi_res", false);
const dc = Cxxrtl.Sample(bool).init(cxxrtl, "spi_dc", false);
const copi = Cxxrtl.Sample(bool).init(cxxrtl, "spi_copi", false);return .{
.clk = clk,
.res = res,
.dc = dc,
.copi = copi,
};
}pub fn tick(self: *SpiConnector) Tick {
const clk = self.clk.tick();
const res = self.res.tick();
const dc = self.dc.tick();
const copi = self.copi.tick();var result: Tick = .Nop;
if (res.curr) {
self.sr = 0;
self.index = 0;
}if (clk.rising()) {
self.sr = (self.sr << 1) | @as(u8, (if (copi.curr) 1 else 0));
if (self.index < 7)
self.index += 1
else if (dc.curr) {
result = .{ .Command = self.sr };
self.index = 0;
} else {
result = .{ .Data = self.sr };
self.index = 0;
}
}return result;
}
```This is a very simple use case. For a relatively overcomplicated one, see
[sh1107]'s [`I2CConnector`].[sh1107]: https://github.com/kivikakk/sh1107
[`I2CConnector`]: https://github.com/kivikakk/sh1107/blob/266adfb0bac55f462393e2ee12610cda321de39a/vsh/src/I2CConnector.zig#L125