diff --git a/bsp/wch/ch32/build.zig b/bsp/wch/ch32/build.zig
index 673fa89..1c5e564 100644
--- a/bsp/wch/ch32/build.zig
+++ b/bsp/wch/ch32/build.zig
@@ -38,6 +38,9 @@ pub const chips = struct {
.svd = path("/src/chips/CH32V00xxx.svd"),
},
},
+ .hal = .{
+ .root_source_file = path("/src/hals/CH32V003xx.zig"),
+ },
};
};
diff --git a/bsp/wch/ch32/build.zig.zon b/bsp/wch/ch32/build.zig.zon
index 84eae81..a334684 100644
--- a/bsp/wch/ch32/build.zig.zon
+++ b/bsp/wch/ch32/build.zig.zon
@@ -10,6 +10,5 @@
"build.zig",
"build.zig.zon",
"src",
- "test",
},
}
diff --git a/bsp/wch/ch32/src/chips/CH32V00xxx.svd b/bsp/wch/ch32/src/chips/CH32V00xxx.svd
index 8eac916..f32e344 100644
--- a/bsp/wch/ch32/src/chips/CH32V00xxx.svd
+++ b/bsp/wch/ch32/src/chips/CH32V00xxx.svd
@@ -5,6 +5,14 @@
CH32V00xxx
1.2
CH32V00xxx View File
+
+ QINGKEV2
+ r0p0
+ little
+ false
+ false
+ 2
+
diff --git a/bsp/wch/ch32/src/cpus/wch_qingke_v2.zig b/bsp/wch/ch32/src/cpus/wch_qingke_v2.zig
index 6a18dc1..61c3e4d 100644
--- a/bsp/wch/ch32/src/cpus/wch_qingke_v2.zig
+++ b/bsp/wch/ch32/src/cpus/wch_qingke_v2.zig
@@ -1,6 +1,7 @@
const std = @import("std");
const root = @import("root");
const microzig = @import("microzig");
+const assert = std.debug.assert;
pub fn enable_interrupts() void {
asm volatile ("csrsi mstatus, 0b1000");
@@ -30,10 +31,13 @@ pub const startup_logic = struct {
:
: [eos] "r" (@as(u32, microzig.config.end_of_stack)),
);
+
root.initialize_system_memories();
+
asm volatile ("csrsi 0x804, 0b111"); // INTSYSCR: enable EABI + Interrupt nesting + HPE
asm volatile ("csrsi mtvec, 0b11"); // mtvec: absolute address + vector table mode
microzig.cpu.enable_interrupts();
+
microzig_main();
}
@@ -45,3 +49,14 @@ pub const startup_logic = struct {
pub fn export_startup_logic() void {
@export(startup_logic._start, .{ .name = "_start" });
}
+
+const VectorTable = microzig.chip.VectorTable;
+pub const vector_table: VectorTable = blk: {
+ var tmp: VectorTable = .{};
+ if (@hasDecl(root, "microzig_options")) {
+ for (@typeInfo(root.VectorTableOptions).Struct.fields) |field|
+ @field(tmp, field.name) = @field(root.microzig_options.interrupts, field.name);
+ }
+
+ break :blk tmp;
+};
diff --git a/bsp/wch/ch32/src/hals/CH32V003xx.zig b/bsp/wch/ch32/src/hals/CH32V003xx.zig
new file mode 100644
index 0000000..aa9f58d
--- /dev/null
+++ b/bsp/wch/ch32/src/hals/CH32V003xx.zig
@@ -0,0 +1,24 @@
+const std = @import("std");
+const mz = @import("microzig");
+const periph = mz.chip.peripherals;
+
+const RCC = periph.RCC;
+const FLASH = periph.FLASH;
+
+pub fn rcc_init_hsi_pll() void {
+ const CFG0_PLL_TRIM: *u8 = @ptrFromInt(0x1FFFF7D4); // Factory HSI clock trim value
+ if (CFG0_PLL_TRIM.* != 0xFF) {
+ RCC.CTLR.modify(.{ .HSITRIM = @as(u5, @truncate(CFG0_PLL_TRIM.*)) });
+ }
+
+ FLASH.ACTLR.modify(.{ .LATENCY = 1 }); // Flash wait state 1 for 48MHz clock
+
+ RCC.CFGR0.modify(.{
+ .PLLSRC = 0, // HSI
+ .HPRE = 0, // Prescaler off
+ });
+ RCC.CTLR.modify(.{ .PLLON = 1 });
+ while (RCC.CTLR.read().PLLRDY != 1) {}
+ RCC.CFGR0.modify(.{ .SW = 0b10 }); // Select PLL clock source
+ while (RCC.CFGR0.read().SWS != 0b10) {} // Spin until PLL selected
+}
diff --git a/examples/wch/ch32/build.zig b/examples/wch/ch32/build.zig
index ec694a8..f80b2b3 100644
--- a/examples/wch/ch32/build.zig
+++ b/examples/wch/ch32/build.zig
@@ -3,7 +3,8 @@ const MicroZig = @import("microzig/build");
const ch32 = @import("microzig/bsp/wch/ch32");
const available_examples = [_]Example{
- .{ .target = ch32.chips.ch32v003xx, .name = "ch32v003xx", .file = "src/empty.zig" },
+ .{ .target = ch32.chips.ch32v003xx, .name = "ch32v003xx_empty", .file = "src/empty.zig" },
+ .{ .target = ch32.chips.ch32v003xx, .name = "ch32v003xx_blinky", .file = "src/blinky.zig" },
};
pub fn build(b: *std.Build) void {
diff --git a/examples/wch/ch32/src/blinky.zig b/examples/wch/ch32/src/blinky.zig
new file mode 100644
index 0000000..cd26260
--- /dev/null
+++ b/examples/wch/ch32/src/blinky.zig
@@ -0,0 +1,15 @@
+const std = @import("std");
+const mz = @import("microzig");
+const periph = mz.chip.peripherals;
+
+pub fn main() !void {
+ mz.hal.rcc_init_hsi_pll();
+ periph.RCC.APB2PCENR.modify(.{ .IOPCEN = 1 });
+ periph.GPIOC.CFGLR.modify(.{ .CNF1 = 0b00, .MODE1 = 0b11 });
+
+ var on: u1 = 0;
+ while (true) {
+ on ^= 1;
+ periph.GPIOC.OUTDR.modify(.{ .ODR1 = on });
+ }
+}
diff --git a/tools/regz/src/Database.zig b/tools/regz/src/Database.zig
index a31f548..e81585b 100644
--- a/tools/regz/src/Database.zig
+++ b/tools/regz/src/Database.zig
@@ -120,6 +120,9 @@ pub const Arch = enum {
// mips
mips,
+ // riscv
+ qingke_v2,
+
pub fn to_string(arch: Arch) []const u8 {
return inline for (@typeInfo(Arch).Enum.fields) |field| {
if (@field(Arch, field.name) == arch)
@@ -176,6 +179,13 @@ pub const Arch = enum {
else => false,
};
}
+
+ pub fn is_riscv(arch: Arch) bool {
+ return switch (arch) {
+ .qingke_v2 => true,
+ else => false,
+ };
+ }
};
// not sure how to communicate the *_once values in generated code
diff --git a/tools/regz/src/arch/riscv.zig b/tools/regz/src/arch/riscv.zig
new file mode 100644
index 0000000..c203a2f
--- /dev/null
+++ b/tools/regz/src/arch/riscv.zig
@@ -0,0 +1,95 @@
+//! codegen specific to riscv
+const std = @import("std");
+const assert = std.debug.assert;
+
+const Database = @import("../Database.zig");
+const Arch = Database.Arch;
+const EntityId = Database.EntityId;
+
+const gen = @import("../gen.zig");
+const InterruptWithIndexAndName = @import("InterruptWithIndexAndName.zig");
+
+const log = std.log.scoped(.@"gen.riscv");
+
+pub fn write_interrupt_vector(
+ db: Database,
+ device_id: EntityId,
+ writer: anytype,
+) !void {
+ assert(db.entity_is("instance.device", device_id));
+ const arch = db.instances.devices.get(device_id).?.arch;
+ assert(arch.is_riscv());
+
+ try writer.writeAll(
+ \\pub const VectorTable = extern struct {
+ \\ const Handler = micro.interrupt.Handler;
+ \\ const unhandled = micro.interrupt.unhandled;
+ \\
+ );
+
+ var index: i32 = 0;
+
+ if (arch == .qingke_v2) { // CPU specific vectors
+ try writer.writeAll(
+ \\ reserved1: [1]u32 = undefined,
+ \\ NMI: Handler = unhandled,
+ \\ EXC: Handler = unhandled,
+ \\ reserved4: [8]u32 = undefined,
+ \\ SysTick: Handler = unhandled,
+ \\ reserved13: [1]u32 = undefined,
+ \\ SWI: Handler = unhandled,
+ \\ reserved15: [1]u32 = undefined,
+ \\
+ );
+ index = 16;
+ }
+
+ if (db.children.interrupts.get(device_id)) |interrupt_set| {
+ var interrupts = std.ArrayList(InterruptWithIndexAndName).init(db.gpa);
+ defer interrupts.deinit();
+
+ var it = interrupt_set.iterator();
+ while (it.next()) |entry| {
+ const interrupt_id = entry.key_ptr.*;
+ const interrupt_index = db.instances.interrupts.get(interrupt_id).?;
+ const name = db.attrs.name.get(interrupt_id) orelse continue;
+
+ try interrupts.append(.{
+ .id = interrupt_id,
+ .name = name,
+ .index = interrupt_index,
+ });
+ }
+
+ std.sort.insertion(
+ InterruptWithIndexAndName,
+ interrupts.items,
+ {},
+ InterruptWithIndexAndName.less_than,
+ );
+
+ for (interrupts.items) |interrupt| {
+ if (index < interrupt.index) {
+ try writer.print("reserved{}: [{}]u32 = undefined,\n", .{
+ index,
+ interrupt.index - index,
+ });
+ index = interrupt.index;
+ } else if (index > interrupt.index) {
+ log.warn("skipping interrupt: {s}", .{interrupt.name});
+ continue;
+ }
+
+ if (db.attrs.description.get(interrupt.id)) |description|
+ try gen.write_comment(db.gpa, description, writer);
+
+ try writer.print("{}: Handler = unhandled,\n", .{
+ std.zig.fmtId(interrupt.name),
+ });
+
+ index += 1;
+ }
+ }
+
+ try writer.writeAll("};\n\n");
+}
diff --git a/tools/regz/src/gen.zig b/tools/regz/src/gen.zig
index 93b7f1c..17d6756 100644
--- a/tools/regz/src/gen.zig
+++ b/tools/regz/src/gen.zig
@@ -9,6 +9,7 @@ const EntitySet = Database.EntitySet;
const arm = @import("arch/arm.zig");
const avr = @import("arch/avr.zig");
+const riscv = @import("arch/riscv.zig");
const log = std.log.scoped(.gen);
@@ -232,6 +233,8 @@ fn write_vector_table(
try arm.write_interrupt_vector(db, device_id, writer)
else if (arch.is_avr())
try avr.write_interrupt_vector(db, device_id, writer)
+ else if (arch.is_riscv())
+ try riscv.write_interrupt_vector(db, device_id, writer)
else if (arch == .unknown)
return
else
diff --git a/tools/regz/src/svd.zig b/tools/regz/src/svd.zig
index 822c44b..94fe3c5 100644
--- a/tools/regz/src/svd.zig
+++ b/tools/regz/src/svd.zig
@@ -215,6 +215,8 @@ fn arch_from_str(str: []const u8) Database.Arch {
.cortex_a57
else if (std.mem.eql(u8, "CA72", str))
.cortex_a72
+ else if (std.mem.eql(u8, "QINGKEV2", str))
+ .qingke_v2
else
.unknown;
}