Introduces the first SVD generated binding for LPC1768. svd2zig contains a hack for missing register sizes.

wch-ch32v003
Felix (xq) Queißner 3 years ago
parent 1aad7b52f6
commit f5bc3be1ae

@ -23,6 +23,8 @@ pub fn build(b: *std.build.Builder) void {
Test{ .name = "blinky", .source = "tests/blinky.zig" }, Test{ .name = "blinky", .source = "tests/blinky.zig" },
}; };
const filter = b.option(std.Target.Cpu.Arch, "filter-target", "Filters for a certain cpu target");
inline for (all_backings) |cfg| { inline for (all_backings) |cfg| {
inline for (all_tests) |tst| { inline for (all_tests) |tst| {
const exe = addEmbeddedExecutable( const exe = addEmbeddedExecutable(
@ -31,10 +33,13 @@ pub fn build(b: *std.build.Builder) void {
tst.source, tst.source,
cfg.backing, cfg.backing,
); );
exe.setBuildMode(mode);
exe.install();
test_step.dependOn(&exe.step); if (filter == null or exe.target.cpu_arch.? == filter.?) {
exe.setBuildMode(mode);
exe.install();
test_step.dependOn(&exe.step);
}
} }
} }
} }
@ -53,9 +58,11 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source:
.name = "chip", .name = "chip",
.path = chip.path, .path = chip.path,
.dependencies = &[_]Pkg{ .dependencies = &[_]Pkg{
pkgs.mmio,
Pkg{ Pkg{
.name = "cpu", .name = "cpu",
.path = chip.cpu.path, .path = chip.cpu.path,
.dependencies = &[_]Pkg{pkgs.mmio},
}, },
Pkg{ Pkg{
.name = "microzig-linker", .name = "microzig-linker",
@ -191,7 +198,7 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source:
Pkg{ Pkg{
.name = "board", .name = "board",
.path = board.path, .path = board.path,
.dependencies = &[_]Pkg{chip_package}, .dependencies = &[_]Pkg{ chip_package, pkgs.mmio },
}, },
}, },
}); });
@ -225,6 +232,11 @@ pub const Backing = union(enum) {
}; };
const pkgs = struct { const pkgs = struct {
const mmio = std.build.Pkg{
.name = "microzig-mmio",
.path = "src/core/mmio.zig",
};
const cpus = struct { const cpus = struct {
const avr5 = Cpu{ const avr5 = Cpu{
.name = "AVR5", .name = "AVR5",

@ -0,0 +1,50 @@
const std = @import("std");
pub fn mmio(addr: usize, comptime size: u8, comptime PackedT: type) *volatile MMIO(size, PackedT) {
return @intToPtr(*volatile MMIO(size, PackedT), addr);
}
pub fn MMIO(comptime size: u8, comptime PackedT: type) type {
if ((size % 8) != 0)
@compileError("size must be divisible by 8!");
if (!std.math.isPowerOfTwo(size / 8))
@compileError("size must encode a power of two number of bytes!");
const IntT = std.meta.Int(.unsigned, size);
if (@sizeOf(PackedT) != (size / 8))
@compileError("IntT and PackedT must have the same size!");
return extern struct {
const Self = @This();
raw: IntT,
pub const underlying_type = PackedT;
pub fn read(addr: *volatile Self) PackedT {
return @bitCast(PackedT, addr.*);
}
pub fn write(addr: *volatile Self, val: PackedT) void {
addr.* = @bitCast(IntT, val);
}
pub fn set(addr: *volatile Self, fields: anytype) void {
var val = read();
inline for (@typeInfo(@TypeOf(fields)).Struct.fields) |field| {
@field(val, field.name) = @field(fields, field.name);
}
write(val);
}
pub fn toggle(addr: *volatile Self, fields: anytype) void {
var val = read();
inline for (@typeInfo(@TypeOf(fields)).Struct.fields) |field| {
@field(val, @tagName(field.default_value.?)) = !@field(val, @tagName(field.default_value.?));
}
write(val);
}
};
}

@ -2,6 +2,7 @@ const std = @import("std");
const micro_linker = @import("microzig-linker"); const micro_linker = @import("microzig-linker");
pub const cpu = @import("cpu"); pub const cpu = @import("cpu");
pub const registers = @import("registers.zig");
pub const memory_regions = [_]micro_linker.MemoryRegion{ pub const memory_regions = [_]micro_linker.MemoryRegion{
micro_linker.MemoryRegion{ .offset = 0x00000000, .length = 512 * 1024, .kind = .flash }, micro_linker.MemoryRegion{ .offset = 0x00000000, .length = 512 * 1024, .kind = .flash },
@ -16,25 +17,37 @@ pub fn parsePin(comptime spec: []const u8) type {
const index = std.mem.indexOfScalar(u8, spec, '.') orelse @compileError(invalid_format_msg); const index = std.mem.indexOfScalar(u8, spec, '.') orelse @compileError(invalid_format_msg);
return struct { const _port: u3 = std.fmt.parseInt(u3, spec[1..index], 10) catch @compileError(invalid_format_msg);
pub const port: u3 = std.fmt.parseInt(u3, spec[1..index], 10) catch @compileError(invalid_format_msg); const _pin: u5 = std.fmt.parseInt(u5, spec[index + 1 ..], 10) catch @compileError(invalid_format_msg);
pub const pin: u5 = std.fmt.parseInt(u5, spec[index + 1 ..], 10) catch @compileError(invalid_format_msg);
const _regs = struct {
const name_suffix = std.fmt.comptimePrint("{d}", .{_port});
const dir = @field(registers.GPIO, "DIR" ++ name_suffix);
const pin = @field(registers.GPIO, "PIN" ++ name_suffix);
const set = @field(registers.GPIO, "SET" ++ name_suffix);
const clr = @field(registers.GPIO, "CLR" ++ name_suffix);
const mask = @field(registers.GPIO, "MASK" ++ name_suffix);
};
const gpio_register = @intToPtr(*volatile registers.GPIO, registers.gpio_base + 0x20 * @as(u32, port)); return struct {
pub const port = _port;
pub const pin = _pin;
pub const regs = _regs;
const gpio_mask: u32 = (1 << pin); const gpio_mask: u32 = (1 << pin);
}; };
} }
pub const gpio = struct { pub const gpio = struct {
pub fn setOutput(comptime pin: type) void { pub fn setOutput(comptime pin: type) void {
pin.gpio_register.dir |= pin.gpio_mask; pin.regs.dir.raw |= pin.gpio_mask;
} }
pub fn setInput(comptime pin: type) void { pub fn setInput(comptime pin: type) void {
pin.gpio_register.dir &= ~pin.gpio_mask; pin.regs.dir.raw &= ~pin.gpio_mask;
} }
pub fn read(comptime pin: type) u1 { pub fn read(comptime pin: type) u1 {
return if ((pin.gpio_register.pin & pin.gpio_mask) != 0) return if ((pin.regs.pin.raw & pin.gpio_mask) != 0)
@as(u1, 1) @as(u1, 1)
else else
0; 0;
@ -42,22 +55,20 @@ pub const gpio = struct {
pub fn write(comptime pin: type, state: u1) void { pub fn write(comptime pin: type, state: u1) void {
if (state == 1) { if (state == 1) {
pin.gpio_register.set = pin.gpio_mask; pin.regs.set.raw = pin.gpio_mask;
} else { } else {
pin.gpio_register.clr = pin.gpio_mask; pin.regs.clr.raw = pin.gpio_mask;
} }
} }
}; };
pub const registers = struct { // pub const GPIO = extern struct {
pub const GPIO = extern struct { // dir: u32, // 0x00
dir: u32, // 0x00 // pad: [3]u32,
pad: [3]u32, // mask: u32, // 0x10
mask: u32, // 0x10 // pin: u32, // 0x14,
pin: u32, // 0x14, // set: u32, // 0x18,
set: u32, // 0x18, // clr: u32, // 0x1C,
clr: u32, // 0x1C, // };
};
pub const gpio_base = 0x2009_C000; // pub const gpio_base = 0x2009_C000;
};

File diff suppressed because it is too large Load Diff

@ -0,0 +1,10 @@
# microzig tools
## `svd2zig.py`
Converts CMSIS SVD files into zig source code. Requires [cmsis_svd](https://github.com/posborne/cmsis-svd/) installed (can be done with `pip install -U cmsis-svd`).
## `linkerscript-gen.zig`
Compiled on-demand by microzig to generate a linkerscript from a board/chip/cpu bundle.

@ -0,0 +1,106 @@
#!/usr/bin/env python3
import sys
import io
import subprocess
import re
from cmsis_svd.parser import SVDParser
def cleanup_description(description):
if description is None:
return ''
return ' '.join(description.replace('\n', ' ').split())
# register names in from Nordic SVDs are using foo[n] for couple of registers
# and also for somereaons there are %s in the reigster names
name_regex = re.compile(r"\[([^\]]+)]")
def cleanup_name(name):
return name_regex.sub(r"_\1", name).replace("%", "_")
class MMIOFileGenerator:
def __init__(self, f):
self.f = f
def generate_padding(self, count, name="padding", offset=0):
while count > 0:
self.write_line(f"{name}{offset + count}: u1 = 0,")
count = count - 1
def generate_register_field(self, field):
'''
returns something like:
name: u<size>, // bit offset: 0 desc: foo description
'''
field.description = cleanup_description(field.description)
field_type = f"u{field.bit_width}" if field.bit_width != 1 else 'bool'
self.write_line(f"{field.name}:{field_type},// bit offset: {field.bit_offset} desc: {field.description}")
return field.bit_offset + field.bit_width
def generate_register_declaration(self, register):
'''
'''
register.description = cleanup_description(register.description)
self.write_line(f"// byte offset: {register.address_offset} {register.description}")
register.name = cleanup_name(register.name)
size = register.size
if size == None:
size = 32 # hack for now...
self.write_line(f" pub const {register.name} = mmio(Address + 0x{register.address_offset:08x}, {size}, packed struct{{")
last_offset = 0
reserved_index = 0
for field in sorted(register.fields, key=lambda f: f.bit_offset):
if last_offset != field.bit_offset:
self.generate_padding(field.bit_offset - last_offset, "reserved", reserved_index)
reserved_index = reserved_index + 1
last_offset = self.generate_register_field(field)
if register.size is not None:
self.generate_padding(register.size - last_offset)
self.write_line("});")
def generate_peripherial_declaration(self, peripherial):
self.write_line(f"pub const {peripherial.name} = extern struct {{")
self.write_line(f"pub const Address: u32 = 0x{peripherial.base_address:08x};")
for register in sorted(peripherial.registers, key=lambda f: f.address_offset):
self.generate_register_declaration(register)
self.write_line("};")
def generate_file(self, device):
self.write_line("// generated using svd2zig.py\n// DO NOT EDIT")
self.write_line(f"// based on {device.name} version {device.version}")
self.write_line("const mmio = @import(\"microzig-mmio\").mmio;")
self.write_line(f"const Name = \"{device.name}\";")
for peripherial in device.peripherals:
self.generate_peripherial_declaration(peripherial)
def write_line(self, line):
self.f.write(line + "\n")
def main():
parser = SVDParser.for_packaged_svd(sys.argv[1], sys.argv[2] + '.svd')
device = parser.get_device()
zig_fmt = subprocess.Popen(('zig', 'fmt', '--stdin'), stdin=subprocess.PIPE,
stdout=subprocess.PIPE, encoding='utf8')
generator = MMIOFileGenerator(zig_fmt.stdin)
generator.generate_file(device)
zig_fmt.stdin.flush()
zig_fmt.stdin.close()
print(zig_fmt.stdout.read())
if __name__ == "__main__":
main()
Loading…
Cancel
Save