Skip to content
Draft
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
2 changes: 2 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const port_list: []const struct {
.{ .name = "rp2xxx", .dep_name = "port/raspberrypi/rp2xxx" },
.{ .name = "stm32", .dep_name = "port/stmicro/stm32" },
.{ .name = "ch32v", .dep_name = "port/wch/ch32v" },
.{ .name = "ch32h", .dep_name = "port/wch/ch32h" },
.{ .name = "msp430", .dep_name = "port/texasinstruments/msp430" },
.{ .name = "mspm0", .dep_name = "port/texasinstruments/mspm0" },
.{ .name = "tm4c", .dep_name = "port/texasinstruments/tm4c" },
Expand Down Expand Up @@ -70,6 +71,7 @@ pub const PortSelect = struct {
rp2xxx: bool = false,
stm32: bool = false,
ch32v: bool = false,
ch32h: bool = false,
msp430: bool = false,
mspm0: bool = false,
tm4c: bool = false,
Expand Down
1 change: 1 addition & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
.@"port/texasinstruments/mspm0" = .{ .path = "port/texasinstruments/mspm0", .lazy = true },
.@"port/texasinstruments/tm4c" = .{ .path = "port/texasinstruments/tm4c", .lazy = true },
.@"port/wch/ch32v" = .{ .path = "port/wch/ch32v", .lazy = true },
.@"port/wch/ch32h" = .{ .path = "port/wch/ch32h", .lazy = true },
},
.paths = .{
"README.md",
Expand Down
22 changes: 22 additions & 0 deletions examples/wch/ch32h/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const std = @import("std");
const microzig = @import("microzig");

const MicroBuild = microzig.MicroBuild(.{
.ch32h = true,
});

pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .small });

const mz_dep = b.dependency("microzig", .{});
const mb = MicroBuild.init(b, mz_dep) orelse return;

const fw = mb.ports.ch32h.addDualCoreFirmware(mb, .{
.name = "dual_blinky",
.v3f_root_source_file = b.path("src/v3f.zig"),
.v5f_root_source_file = b.path("src/v5f.zig"),
.optimize = optimize,
});

mb.ports.ch32h.installFirmware(mb, fw);
}
14 changes: 14 additions & 0 deletions examples/wch/ch32h/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.{
.name = .examples_wch_ch32h,
.fingerprint = 0x98c6b66930eca2b,
.version = "0.0.0",
.dependencies = .{
.microzig = .{ .path = "../../.." },
},

.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}
38 changes: 38 additions & 0 deletions examples/wch/ch32h/src/v3f.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const microzig = @import("microzig");
const std = @import("std");

pub const panic = microzig.panic;

pub const std_options = microzig.std_options(.{});

comptime {
_ = microzig.export_startup();
}

const RCC = microzig.chip.peripherals.RCC;
const PFIC = microzig.chip.peripherals.PFIC;
const GPIOC = microzig.chip.peripherals.GPIOC;

const pin: u4 = 2;

fn delay(cycles: u32) void {
for (0..cycles) |_| {
asm volatile ("nop");
}
}

pub fn main() !void {
RCC.HB2PCENR.modify(.{ .IOPCEN = 1 });

GPIOC.CFGLR.modify(.{ .MODE2 = 0b11, .CNF2 = 0b01 });

PFIC.WAKEIP1.raw = 0x10000 & ~@as(u32, 0x3FF);
PFIC.SCTLR.raw |= (1 << 5);

while (true) {
microzig.hal.gpio.write(GPIOC, pin, 1);
delay(10_000);
microzig.hal.gpio.write(GPIOC, pin, 0);
delay(10_000);
}
}
33 changes: 33 additions & 0 deletions examples/wch/ch32h/src/v5f.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const microzig = @import("microzig");

pub const panic = microzig.panic;

pub const std_options = microzig.std_options(.{});

comptime {
_ = microzig.export_startup();
}

const RCC = microzig.chip.peripherals.RCC;
const GPIOC = microzig.chip.peripherals.GPIOC;

const pin: u4 = 3;

fn delay(cycles: u32) void {
for (0..cycles) |_| {
asm volatile ("nop");
}
}

pub fn main() !void {
RCC.HB2PCENR.modify(.{ .IOPCEN = 1 });

GPIOC.CFGLR.modify(.{ .MODE3 = 0b11, .CNF3 = 0b01 });

while (true) {
microzig.hal.gpio.write(GPIOC, pin, 1);
delay(20_000);
microzig.hal.gpio.write(GPIOC, pin, 0);
delay(20_000);
}
}
193 changes: 193 additions & 0 deletions port/wch/ch32h/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
const std = @import("std");
const microzig = @import("microzig/build-internals");

const Self = @This();

const KiB = 1024;

pub fn build(b: *std.Build) void {
_ = b;
}

pub const default_v5f_image_offset: u64 = 0x10000;
pub const total_flash_size: u64 = 960 * KiB;

chips: struct {
ch32h417_v3f: *const microzig.Target,
ch32h417_v5f: *const microzig.Target,
},

boards: struct {},

fn create_core(
dep: *std.Build.Dependency,
cpu_name: []const u8,
cpu_impl_file: std.Build.LazyPath,
memory_regions: []const microzig.MemoryRegion,
) *microzig.Target {
const b = dep.builder;

const cpu_imports = b.allocator.dupe(std.Build.Module.Import, &.{
.{
.name = "riscv32-common",
.module = b.dependency("microzig/modules/riscv32-common", .{}).module("riscv32-common"),
},
.{
.name = "cpu_impl",
.module = std.Build.Module.create(b, .{ .root_source_file = cpu_impl_file }),
},
}) catch @panic("out of memory");

const core = b.allocator.create(microzig.Target) catch @panic("out of memory");
core.* = .{
.dep = dep,
.preferred_binary_format = .binary,
.zig_target = .{
.cpu_arch = .riscv32,
.cpu_model = .{ .explicit = &std.Target.riscv.cpu.generic_rv32 },
.cpu_features_add = cpu_common_features,
.os_tag = .freestanding,
.abi = .eabi,
},
.cpu = .{
.name = cpu_name,
.root_source_file = dep.path("src/cpus/main.zig"),
.imports = cpu_imports,
},
.chip = .{
.name = "CH32H417",
.url = "https://www.wch-ic.com/products/CH32H417.html",
.register_definition = .{ .svd = b.path("src/chips/ch32h417.svd") },
.memory_regions = memory_regions,
},
.hal = .{
.root_source_file = dep.path("src/hals/ch32h417.zig"),
},
};

return core;
}

const cpu_common_features = std.Target.riscv.featureSet(&.{
.i, .m, .a, .f, .c, .b, .xwchc, .zicsr, .zifencei,
});

pub fn init(dep: *std.Build.Dependency) ?Self {
const chip_v3f = create_core(dep, "qingkev3f", dep.path("src/cpus/qingkev3f.zig"), &.{
.{ .name = "FLASH", .tag = .flash, .offset = 0x0000_0000, .length = default_v5f_image_offset, .access = .rx },
.{ .name = "SRAM", .tag = .ram, .offset = 0x2010_0000, .length = 512 * KiB, .access = .rwx },
});
const chip_v5f = create_core(dep, "qingkev5f", dep.path("src/cpus/qingkev5f.zig"), &.{
// V5F image lives right behind the V3F image in the merged binary.
.{ .name = "FLASH", .tag = .flash, .offset = default_v5f_image_offset, .length = total_flash_size - default_v5f_image_offset, .access = .rx },
// DTCM: 256 KB zero-wait data RAM, private to the V5F.
// Listed first so the default stack lands at its end.
.{ .name = "DTCM", .tag = .ram, .offset = 0x200C_0000, .length = 256 * KiB, .access = .rw },
// ITCM: 128 KB zero-wait code RAM, private to the V5F.
.{ .name = "ITCM", .tag = .ram, .offset = 0x200A_0000, .length = 128 * KiB, .access = .rwx },
});

return .{
.chips = .{
.ch32h417_v3f = chip_v3f,
.ch32h417_v5f = chip_v5f,
},
.boards = .{},
};
}

/// Options for building a dual-core (V3F + V5F) firmware image.
pub const DualCoreFirmwareOptions = struct {
/// Base name of the firmware; the per-core executables are named
/// `<name>_v3f` / `<name>_v5f`, the merged image `<name>.bin`.
name: []const u8,

/// Root source file of the V3F (boot core) application, e.g. `src/v3f.zig`.
v3f_root_source_file: std.Build.LazyPath,

/// Root source file of the V5F (application core) application, e.g. `src/v5f.zig`.
v5f_root_source_file: std.Build.LazyPath,

/// Optimization level for both cores.
optimize: std.builtin.OptimizeMode,
};

/// A pair of firmware builds plus their merged flash image.
pub fn DualCoreFirmware(comptime mb_type: type) type {
const Firmware = std.meta.Child(mb_type).Firmware;
return struct {
/// Firmware's name.
name: []const u8,
/// Firmware running on the V3F boot core (CORE0).
v3f: *Firmware,
/// Firmware running on the V5F application core (CORE1).
v5f: *Firmware,
/// Merged flash image.
merged_bin: std.Build.LazyPath,
};
}

/// Builds the V3F and V5F applications as two independent firmwares and merges
/// both ELF images into one flash image, gap padded with 0xFF.
pub fn addDualCoreFirmware(
self: Self,
mb: anytype,
options: DualCoreFirmwareOptions,
) DualCoreFirmware(@TypeOf(mb)) {
const b = mb.builder;

const fw_v3f = mb.add_firmware(.{
.name = b.fmt("{s}_v3f", .{options.name}),
.root_source_file = options.v3f_root_source_file,
.target = self.chips.ch32h417_v3f,
.optimize = options.optimize,
});

const fw_v5f = mb.add_firmware(.{
.name = b.fmt("{s}_v5f", .{options.name}),
.root_source_file = options.v5f_root_source_file,
.target = self.chips.ch32h417_v5f,
.optimize = options.optimize,
});

// Host tool that merges the per-core ELF images into one flash image.
const merge_exe = b.addExecutable(.{
.name = "merge_dual_core_image",
.root_module = b.createModule(.{
.root_source_file = self.chips.ch32h417_v3f.dep.path("tools/merge_dual_core_image.zig"),
.target = b.graph.host,
.optimize = .ReleaseSafe,
}),
});

const merge_run = b.addRunArtifact(merge_exe);
merge_run.addFileArg(fw_v3f.get_emitted_elf());
merge_run.addFileArg(fw_v5f.get_emitted_elf());
merge_run.addArg(b.fmt("0x{x}", .{default_v5f_image_offset}));
const merged_bin = merge_run.addOutputFileArg(b.fmt("{s}.bin", .{options.name}));

return .{
.name = options.name,
.v3f = fw_v3f,
.v5f = fw_v5f,
.merged_bin = merged_bin,
};
}

/// Install the merged image at `firmware/<name>.bin`.
pub fn installFirmware(
self: Self,
mb: anytype,
firmware: DualCoreFirmware(@TypeOf(mb)),
) void {
_ = self;

const b = mb.builder;

const install = b.addInstallFileWithDir(
firmware.merged_bin,
.{ .custom = "firmware" },
b.fmt("{s}.bin", .{firmware.name}),
);
b.getInstallStep().dependOn(&install.step);
}
16 changes: 16 additions & 0 deletions port/wch/ch32h/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.{
.name = .mz_port_wch_ch32h,
.version = "0.0.0",
.fingerprint = 0x731af18d92d6c086,
.dependencies = .{
.@"microzig/build-internals" = .{ .path = "../../../build-internals" },
.@"microzig/modules/riscv32-common" = .{ .path = "../../../modules/riscv32-common" },
},
.paths = .{
"README.md",
"build.zig",
"build.zig.zon",
"src",
"tools",
},
}
Loading
Loading