Skip to content
This repository was archived by the owner on Apr 21, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v2
- uses: goto-bus-stop/[email protected]
- name: Checkout
uses: actions/checkout@v4

- name: Setup Zig
uses: mlugg/setup-zig@v1
with:
version: 0.11.0
version: 0.14.0

- name: Build
run: zig build install

- name: Test Suite
- name: Run Test Suite
run: zig build test

- name: Run Samples
run: |
./zig-out/bin/aviron --info
./zig-out/bin/aviron --trace zig-out/samples/math.elf
./zig-out/bin/aviron zig-out/samples/hello-world.elf

3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
zig-*
zig-out
.zig-cache
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

AVR simulator in Zig.

> DISCLAIMER: This is work in project and can currently emulate a good amount of the AVR instruction set
> without cycle counting.
> DISCLAIMER: This is work in project and can currently emulate a good amount of the AVR instruction set without cycle counting.

## Development

Use [Zig 0.14.0](https://ziglang.org/download/#release-0.14.0) to compile AViRon.

### Repo Architecture

```sh
Expand Down
159 changes: 108 additions & 51 deletions build.zig
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
const std = @import("std");
const Build = std.Build;
const LazyPath = Build.LazyPath;
const ResolvedTarget = Build.ResolvedTarget;

const TestSuiteConfig = @import("src/testconfig.zig").TestSuiteConfig;

const samples = [_][]const u8{
"math",
"hello-world",
};

const avr_target = std.zig.CrossTarget{
const avr_target_query = std.Target.Query{
.cpu_arch = .avr,
.cpu_model = .{ .explicit = &std.Target.avr.cpu.atmega328p },
.os_tag = .freestanding,
Expand All @@ -15,7 +20,7 @@ const avr_target = std.zig.CrossTarget{
// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) !void {
pub fn build(b: *Build) !void {
// Targets
const test_step = b.step("test", "Run test suite");
const run_step = b.step("run", "Run the app");
Expand All @@ -36,31 +41,35 @@ pub fn build(b: *std.Build) !void {
// Modules

const isa_module = b.createModule(.{
.source_file = .{ .path = "src/shared/isa.zig" },
.root_source_file = b.path("src/shared/isa.zig"),
});
const isa_tables_module = b.createModule(.{
.source_file = generateIsaTables(b, isa_module),
.dependencies = &.{
.root_source_file = generateIsaTables(b, isa_module),
.imports = &.{
.{ .name = "isa", .module = isa_module },
},
});
const aviron_module = b.addModule("aviron", .{
.source_file = .{ .path = "src/lib/aviron.zig" },
.dependencies = &.{
.root_source_file = b.path("src/lib/aviron.zig"),
.imports = &.{
.{ .name = "autogen-tables", .module = isa_tables_module },
.{ .name = "isa", .module = isa_module },
},
});

const testsuite_module = b.addModule("aviron-testsuite", .{
.root_source_file = b.path("src/libtestsuite/lib.zig"),
});

// Main emulator executable
const aviron_exe = b.addExecutable(.{
.name = "aviron",
.root_source_file = .{ .path = "src/main.zig" },
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
aviron_exe.addModule("args", args_module);
aviron_exe.addModule("aviron", aviron_module);
aviron_exe.root_module.addImport("args", args_module);
aviron_exe.root_module.addImport("aviron", aviron_module);
b.installArtifact(aviron_exe);

const run_cmd = b.addRunArtifact(aviron_exe);
Expand All @@ -70,17 +79,20 @@ pub fn build(b: *std.Build) !void {
}
run_step.dependOn(&run_cmd.step);

const avr_target = b.resolveTargetQuery(avr_target_query);

// Samples
for (samples) |sample_name| {
const sample = b.addExecutable(.{
.name = sample_name,
.root_source_file = .{ .path = b.fmt("samples/{s}.zig", .{sample_name}) },
.root_source_file = b.path(b.fmt("samples/{s}.zig", .{sample_name})),
.target = avr_target,
.optimize = .ReleaseSmall,
.strip = false,
});
sample.bundle_compiler_rt = false;
sample.setLinkerScriptPath(std.build.FileSource{ .path = "linker.ld" });
sample.strip = false;
sample.setLinkerScript(b.path("linker.ld"));
sample.root_module.addImport("testsuite", testsuite_module);

// install to the prefix:
const install_elf_sample = b.addInstallFile(sample.getEmittedBin(), b.fmt("samples/{s}.elf", .{sample_name}));
Expand All @@ -89,40 +101,43 @@ pub fn build(b: *std.Build) !void {

// Test suite:

try addTestSuite(b, test_step, debug_testsuite_step, target, optimize, args_module, aviron_module);
try addTestSuite(b, test_step, debug_testsuite_step, target, avr_target, optimize, args_module, aviron_module);

try addTestSuiteUpdate(b, update_testsuite_step);
}

fn addTestSuite(
b: *std.Build,
test_step: *std.Build.Step,
debug_step: *std.Build.Step,
target: std.zig.CrossTarget,
b: *Build,
test_step: *Build.Step,
debug_step: *Build.Step,
host_target: ResolvedTarget,
avr_target: ResolvedTarget,
optimize: std.builtin.OptimizeMode,
args_module: *std.build.Module,
aviron_module: *std.build.Module,
args_module: *Build.Module,
aviron_module: *Build.Module,
) !void {
const unit_tests = b.addTest(.{
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.root_source_file = b.path("src/main.zig"),
.target = host_target,
.optimize = optimize,
});
test_step.dependOn(&b.addRunArtifact(unit_tests).step);

const testrunner_exe = b.addExecutable(.{
.name = "aviron-test-runner",
.root_source_file = .{ .path = "src/testrunner.zig" },
.target = target,
.root_source_file = b.path("src/testrunner.zig"),
.target = host_target,
.optimize = optimize,
});
testrunner_exe.addModule("args", args_module);
testrunner_exe.addModule("aviron", aviron_module);
testrunner_exe.root_module.addImport("args", args_module);
testrunner_exe.root_module.addImport("aviron", aviron_module);

debug_step.dependOn(&b.addInstallArtifact(testrunner_exe, .{}).step);

{
var walkdir = try b.build_root.handle.openIterableDir("testsuite", .{});
var walkdir = try b.build_root.handle.openDir("testsuite", .{
.iterate = true,
});
defer walkdir.close();

var walker = try walkdir.walk(b.allocator);
Expand All @@ -132,6 +147,11 @@ fn addTestSuite(
if (entry.kind != .file)
continue;

if (std.mem.eql(u8, entry.path, "dummy.zig")) {
// This file is not interesting to test.
continue;
}

const FileAction = union(enum) {
compile,
load,
Expand Down Expand Up @@ -162,7 +182,7 @@ fn addTestSuite(
} else .unknown;

const ConfigAndExe = struct {
binary: std.Build.LazyPath,
binary: LazyPath,
config: TestSuiteConfig,
};

Expand All @@ -177,26 +197,58 @@ fn addTestSuite(
const config = try parseTestSuiteConfig(b, file);

const custom_target = if (config.cpu) |cpu|
std.zig.CrossTarget.parse(.{
b.resolveTargetQuery(std.Target.Query.parse(.{
.arch_os_abi = "avr-freestanding-eabi",
.cpu_features = cpu,
}) catch @panic(cpu)
}) catch @panic(cpu))
else
avr_target;

const file_ext = std.fs.path.extension(entry.path);
const is_zig_test = std.mem.eql(u8, file_ext, ".zig");
const is_c_test = std.mem.eql(u8, file_ext, ".c");
const is_asm_test = std.mem.eql(u8, file_ext, ".S");

std.debug.assert(is_zig_test or is_c_test or is_asm_test);

const source_file = b.path(b.fmt("testsuite/{s}", .{entry.path}));
const root_file = if (is_zig_test)
source_file
else
b.path("testsuite/dummy.zig");

const test_payload = b.addExecutable(.{
.name = std.fs.path.stem(entry.basename),
.root_source_file = .{ .path = b.fmt("testsuite/{s}", .{entry.path}) },
.target = custom_target,
.optimize = config.optimize,
.strip = false,
.root_source_file = if (is_zig_test) root_file else null,
.link_libc = false,
});
test_payload.want_lto = false; // AVR has no LTO support!
test_payload.verbose_link = true;
test_payload.verbose_cc = true;
test_payload.bundle_compiler_rt = false;
test_payload.addIncludePath(.{ .path = "testsuite" });
test_payload.setLinkerScriptPath(std.build.FileSource{ .path = "linker.ld" });
test_payload.addAnonymousModule("testsuite", .{
.source_file = .{ .path = "src/libtestsuite/lib.zig" },
});
test_payload.strip = false;

test_payload.setLinkerScript(b.path("linker.ld"));

if (is_c_test or is_asm_test) {
test_payload.addIncludePath(b.path("testsuite"));
}
if (is_c_test) {
test_payload.addCSourceFile(.{
.file = source_file,
.flags = &.{},
});
}
if (is_asm_test) {
test_payload.addAssemblyFile(source_file);
}
if (is_zig_test) {
test_payload.root_module.addAnonymousImport("testsuite", .{
.root_source_file = b.path("src/libtestsuite/lib.zig"),
});
}

debug_step.dependOn(&b.addInstallFile(
test_payload.getEmittedBin(),
Expand All @@ -219,7 +271,7 @@ fn addTestSuite(
} else |_| @panic(config_path);

break :blk ConfigAndExe{
.binary = .{ .path = b.fmt("testsuite/{s}", .{entry.path}) },
.binary = b.path(b.fmt("testsuite/{s}", .{entry.path})),
.config = config,
};
},
Expand All @@ -229,7 +281,7 @@ fn addTestSuite(

const test_run = b.addRunArtifact(testrunner_exe);
test_run.addArg("--config");
test_run.addFileArg(write_file.files.items[0].getPath());
test_run.addFileArg(write_file.getDirectory().path(b, "config.json"));
test_run.addArg("--name");
test_run.addArg(entry.path);

Expand All @@ -242,16 +294,20 @@ fn addTestSuite(
}
}

fn addTestSuiteUpdate(b: *std.Build, invoke_step: *std.Build.Step) !void {
const avr_gcc = if (b.findProgram(&.{"avr-gcc"}, &.{})) |path| std.build.LazyPath{
fn addTestSuiteUpdate(
b: *Build,
invoke_step: *Build.Step,
) !void {
const avr_gcc = if (b.findProgram(&.{"avr-gcc"}, &.{})) |path| LazyPath{
.cwd_relative = path,
} else |_| b.addExecutable(.{
.name = "no-avr-gcc",
.root_source_file = .{ .path = "tools/no-avr-gcc.zig" },
.target = b.graph.host,
.root_source_file = b.path("tools/no-avr-gcc.zig"),
}).getEmittedBin();

{
var walkdir = try b.build_root.handle.openIterableDir("testsuite.avr-gcc", .{});
var walkdir = try b.build_root.handle.openDir("testsuite.avr-gcc", .{ .iterate = true });
defer walkdir.close();

var walker = try walkdir.walk(b.allocator);
Expand Down Expand Up @@ -295,11 +351,12 @@ fn addTestSuiteUpdate(b: *std.Build, invoke_step: *std.Build.Step) !void {

const config = try parseTestSuiteConfig(b, file);

const gcc_invocation = std.Build.Step.Run.create(b, "run avr-gcc");
const gcc_invocation = Build.Step.Run.create(b, "run avr-gcc");
gcc_invocation.addFileArg(avr_gcc);
gcc_invocation.addArg("-o");
gcc_invocation.addArg(b.fmt("testsuite/{s}/{s}.elf", .{ std.fs.path.dirname(entry.path).?, std.fs.path.stem(entry.basename) }));
gcc_invocation.addArg(b.fmt("-mmcu={s}", .{config.cpu orelse avr_target.cpu_model.explicit.llvm_name orelse @panic("Unknown MCU!")}));
gcc_invocation.addArg(b.fmt("-mmcu={s}", .{config.cpu orelse @panic("Uknown MCU!")}));
//avr_target.cpu_model.explicit.llvm_name orelse @panic("Unknown MCU!")}));
for (config.gcc_flags) |opt| {
gcc_invocation.addArg(opt);
}
Expand All @@ -310,7 +367,7 @@ fn addTestSuiteUpdate(b: *std.Build, invoke_step: *std.Build.Step) !void {
const write_file = b.addWriteFile("config.json", config.toString(b));

const copy_file = b.addSystemCommand(&.{"cp"}); // todo make this cross-platform!
copy_file.addFileArg(write_file.files.items[0].getPath());
copy_file.addFileArg(write_file.getDirectory().path(b, "config.json"));
copy_file.addArg(b.fmt("testsuite/{s}/{s}.elf.json", .{ std.fs.path.dirname(entry.path).?, std.fs.path.stem(entry.basename) }));

invoke_step.dependOn(&gcc_invocation.step);
Expand All @@ -321,7 +378,7 @@ fn addTestSuiteUpdate(b: *std.Build, invoke_step: *std.Build.Step) !void {
}
}

fn parseTestSuiteConfig(b: *std.Build, file: std.fs.File) !TestSuiteConfig {
fn parseTestSuiteConfig(b: *Build, file: std.fs.File) !TestSuiteConfig {
var code = std.ArrayList(u8).init(b.allocator);
defer code.deinit();

Expand Down Expand Up @@ -355,14 +412,14 @@ fn parseTestSuiteConfig(b: *std.Build, file: std.fs.File) !TestSuiteConfig {
);
}

fn generateIsaTables(b: *std.Build, isa_mod: *std.Build.Module) std.Build.LazyPath {
fn generateIsaTables(b: *Build, isa_mod: *Build.Module) LazyPath {
const generate_tables_exe = b.addExecutable(.{
.name = "aviron-generate-tables",
.root_source_file = .{ .path = "tools/generate-tables.zig" },
.target = .{},
.root_source_file = b.path("tools/generate-tables.zig"),
.target = b.graph.host,
.optimize = .Debug,
});
generate_tables_exe.addModule("isa", isa_mod);
generate_tables_exe.root_module.addImport("isa", isa_mod);

const run = b.addRunArtifact(generate_tables_exe);

Expand Down
Loading