You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

93 lines
3.7 KiB
Zig

const std = @import("std");
const xml = @import("xml.zig");
const Peripheral = @import("Peripheral.zig");
const Register = @import("Register.zig");
const Field = @import("Field.zig");
const ArenaAllocator = std.heap.ArenaAllocator;
const Allocator = std.mem.Allocator;
pub fn parsePeripheral(arena: *ArenaAllocator, node: *xml.Node) !Peripheral {
const allocator = arena.allocator();
return Peripheral{
.name = try allocator.dupe(u8, xml.getAttribute(node, "name") orelse return error.NoName),
.version = if (xml.getAttribute(node, "version")) |version|
try allocator.dupe(u8, version)
else
null,
.description = if (xml.getAttribute(node, "caption")) |caption|
try allocator.dupe(u8, caption)
else
null,
// TODO: make sure this is always null
.base_addr = null,
};
}
pub fn parseRegister(arena: *ArenaAllocator, node: *xml.Node, regs_start_addr: usize, has_fields: bool) !Register {
const allocator = arena.allocator();
return Register{
.name = try allocator.dupe(u8, xml.getAttribute(node, "name") orelse return error.NoName),
.description = if (xml.getAttribute(node, "caption")) |caption|
try allocator.dupe(u8, caption)
else
null,
.addr_offset = if (xml.getAttribute(node, "offset")) |reg_offset_str|
regs_start_addr + try std.fmt.parseInt(usize, reg_offset_str, 0)
else
return error.NoAddrOffset,
.size = if (xml.getAttribute(node, "size")) |size_str| blk: {
const full_size = 8 * try std.fmt.parseInt(usize, size_str, 0);
if (!has_fields) {
const mask = try std.fmt.parseInt(u64, xml.getAttribute(node, "mask") orelse break :blk full_size, 0);
// ensure it starts at bit 0
if (mask & 0x1 == 0)
break :blk full_size;
var cursor: u7 = 0;
while ((mask & (@as(u64, 1) << @intCast(u6, cursor))) != 0 and cursor < @bitSizeOf(u64)) : (cursor += 1) {}
// we now have width, make sure that the mask is contiguous
const width = cursor;
while (cursor < @bitSizeOf(u64)) : (cursor += 1) {
if ((mask & (@as(u64, 1) << @intCast(u6, cursor))) != 0) {
break :blk full_size;
}
} else break :blk width;
}
break :blk full_size;
} else return error.NoSize, // if this shows up then we need to determine the default size of a register
};
}
pub fn parseField(arena: *ArenaAllocator, node: *xml.Node) !Field {
const allocator = arena.allocator();
const name = try allocator.dupe(u8, xml.getAttribute(node, "name") orelse return error.NoName);
const mask = try std.fmt.parseInt(u64, xml.getAttribute(node, "mask") orelse return error.NoMask, 0);
var offset: u7 = 0;
while ((mask & (@as(u64, 1) << @intCast(u6, offset))) == 0 and offset < @bitSizeOf(usize)) : (offset += 1) {}
var cursor: u7 = offset;
while ((mask & (@as(u64, 1) << @intCast(u6, cursor))) != 0 and cursor < @bitSizeOf(u64)) : (cursor += 1) {}
const width = cursor - offset;
while (cursor < @bitSizeOf(u64)) : (cursor += 1)
if ((mask & (@as(u64, 1) << @intCast(u6, cursor))) != 0) {
std.log.warn("found mask with discontinuous bits: {s}, ignoring", .{name});
return error.InvalidMask;
};
return Field{
.name = name,
.description = if (xml.getAttribute(node, "caption")) |caption|
try allocator.dupe(u8, caption)
else
null,
.offset = offset,
.width = width,
};
}