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" },
};
const filter = b.option(std.Target.Cpu.Arch, "filter-target", "Filters for a certain cpu target");
inline for (all_backings) |cfg| {
inline for (all_tests) |tst| {
const exe = addEmbeddedExecutable(
@ -31,6 +33,8 @@ pub fn build(b: *std.build.Builder) void {
tst.source,
cfg.backing,
);
if (filter == null or exe.target.cpu_arch.? == filter.?) {
exe.setBuildMode(mode);
exe.install();
@ -38,6 +42,7 @@ pub fn build(b: *std.build.Builder) void {
}
}
}
}
fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source: []const u8, backing: Backing) *std.build.LibExeObjStep {
const Pkg = std.build.Pkg;
@ -53,9 +58,11 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source:
.name = "chip",
.path = chip.path,
.dependencies = &[_]Pkg{
pkgs.mmio,
Pkg{
.name = "cpu",
.path = chip.cpu.path,
.dependencies = &[_]Pkg{pkgs.mmio},
},
Pkg{
.name = "microzig-linker",
@ -191,7 +198,7 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source:
Pkg{
.name = "board",
.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 mmio = std.build.Pkg{
.name = "microzig-mmio",
.path = "src/core/mmio.zig",
};
const cpus = struct {
const avr5 = Cpu{
.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");
pub const cpu = @import("cpu");
pub const registers = @import("registers.zig");
pub const memory_regions = [_]micro_linker.MemoryRegion{
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);
return struct {
pub const port: u3 = std.fmt.parseInt(u3, spec[1..index], 10) catch @compileError(invalid_format_msg);
pub const pin: u5 = std.fmt.parseInt(u5, spec[index + 1 ..], 10) catch @compileError(invalid_format_msg);
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);
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);
};
}
pub const gpio = struct {
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 {
pin.gpio_register.dir &= ~pin.gpio_mask;
pin.regs.dir.raw &= ~pin.gpio_mask;
}
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)
else
0;
@ -42,22 +55,20 @@ pub const gpio = struct {
pub fn write(comptime pin: type, state: u1) void {
if (state == 1) {
pin.gpio_register.set = pin.gpio_mask;
pin.regs.set.raw = pin.gpio_mask;
} else {
pin.gpio_register.clr = pin.gpio_mask;
pin.regs.clr.raw = pin.gpio_mask;
}
}
};
pub const registers = struct {
pub const GPIO = extern struct {
dir: u32, // 0x00
pad: [3]u32,
mask: u32, // 0x10
pin: u32, // 0x14,
set: u32, // 0x18,
clr: u32, // 0x1C,
};
// pub const GPIO = extern struct {
// dir: u32, // 0x00
// pad: [3]u32,
// mask: u32, // 0x10
// pin: u32, // 0x14,
// set: u32, // 0x18,
// 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