generated from sirlilpanda/kicad-project-template-actionless
327 lines
11 KiB
Zig
327 lines
11 KiB
Zig
const sys = @import("sys");
|
|
const std = @import("std");
|
|
const errors = @import("error");
|
|
const builtin = @import("builtin");
|
|
|
|
// read: https://github.com/espressif/esp-idf/blob/97d95853572ab74f4769597496af9d5fe8b6bdea/components/heap/include/esp_heap_caps.h#L29-L53
|
|
// ---------------------------------------------------------------------------
|
|
// Caps — packed struct matching esp_heap_caps.h bit positions exactly.
|
|
//
|
|
// Bit layout (matches MALLOC_CAP_* defines):
|
|
// 0 exec (only when CONFIG_HEAP_HAS_EXEC_HEAP)
|
|
// 1 32bit
|
|
// 2 8bit
|
|
// 3 dma
|
|
// 4- 9 pid2..pid7
|
|
// 10 spiram
|
|
// 11 internal
|
|
// 12 default
|
|
// 13 iram_8bit
|
|
// 14 retention
|
|
// 15 rtcram
|
|
// 16 tcm
|
|
// 17 dma_desc_ahb
|
|
// 18 dma_desc_axi
|
|
// 19 cache_aligned
|
|
// 20 simd
|
|
// 21-30 (reserved)
|
|
// 31 invalid
|
|
// ---------------------------------------------------------------------------
|
|
pub const Caps = packed struct(u32) {
|
|
exec: bool = false, // bit 0 — requires CONFIG_HEAP_HAS_EXEC_HEAP
|
|
@"32bit": bool = false, // bit 1
|
|
@"8bit": bool = false, // bit 2
|
|
dma: bool = false, // bit 3
|
|
pid2: bool = false, // bit 4
|
|
pid3: bool = false, // bit 5
|
|
pid4: bool = false, // bit 6
|
|
pid5: bool = false, // bit 7
|
|
pid6: bool = false, // bit 8
|
|
pid7: bool = false, // bit 9
|
|
spiram: bool = false, // bit 10
|
|
internal: bool = false, // bit 11
|
|
default: bool = false, // bit 12
|
|
iram_8bit: bool = false, // bit 13
|
|
retention: bool = false, // bit 14
|
|
rtcram: bool = false, // bit 15
|
|
tcm: bool = false, // bit 16
|
|
dma_desc_ahb: bool = false, // bit 17
|
|
dma_desc_axi: bool = false, // bit 18
|
|
cache_aligned: bool = false, // bit 19
|
|
simd: bool = false, // bit 20
|
|
_reserved: u10 = 0, // bits 21-30
|
|
invalid: bool = false, // bit 31
|
|
|
|
/// Cast to the raw u32 value the heap_caps_* C functions expect.
|
|
pub fn toRaw(self: Caps) u32 {
|
|
return @bitCast(self);
|
|
}
|
|
|
|
/// Re-hydrate from a raw C bitmask (e.g. value returned by a C API).
|
|
pub fn fromRaw(raw: u32) Caps {
|
|
return @bitCast(raw);
|
|
}
|
|
|
|
// -- Named presets matching common ESP-IDF usage patterns ---------------
|
|
|
|
/// General-purpose heap (equivalent to malloc/calloc).
|
|
pub const default_caps: Caps = .{ .default = true };
|
|
/// Internal RAM, byte-addressable.
|
|
pub const internal_caps: Caps = .{ .internal = true, .@"8bit" = true };
|
|
/// DMA-capable internal RAM.
|
|
pub const dma_caps: Caps = .{ .dma = true, .@"8bit" = true, .internal = true };
|
|
/// External SPI RAM, byte-addressable.
|
|
pub const spiram_caps: Caps = .{ .spiram = true, .@"8bit" = true };
|
|
/// RTC fast memory (survives deep sleep).
|
|
pub const rtcram_caps: Caps = .{ .rtcram = true };
|
|
/// Tightly-coupled memory.
|
|
pub const tcm_caps: Caps = .{ .tcm = true };
|
|
/// Executable memory (requires CONFIG_HEAP_HAS_EXEC_HEAP).
|
|
pub const exec_caps: Caps = .{ .exec = true };
|
|
/// Cache-line aligned memory.
|
|
pub const cache_aligned_caps: Caps = .{ .cache_aligned = true, .default = true };
|
|
};
|
|
|
|
// Verify the bit layout matches the C header at compile time.
|
|
comptime {
|
|
std.debug.assert(@as(u32, @bitCast(Caps{ .exec = true })) == (1 << 0));
|
|
std.debug.assert(@as(u32, @bitCast(Caps{ .@"32bit" = true })) == (1 << 1));
|
|
std.debug.assert(@as(u32, @bitCast(Caps{ .@"8bit" = true })) == (1 << 2));
|
|
std.debug.assert(@as(u32, @bitCast(Caps{ .dma = true })) == (1 << 3));
|
|
std.debug.assert(@as(u32, @bitCast(Caps{ .spiram = true })) == (1 << 10));
|
|
std.debug.assert(@as(u32, @bitCast(Caps{ .internal = true })) == (1 << 11));
|
|
std.debug.assert(@as(u32, @bitCast(Caps{ .default = true })) == (1 << 12));
|
|
std.debug.assert(@as(u32, @bitCast(Caps{ .simd = true })) == (1 << 20));
|
|
std.debug.assert(@as(u32, @bitCast(Caps{ .invalid = true })) == (1 << 31));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// HeapCapsAllocator
|
|
// ---------------------------------------------------------------------------
|
|
|
|
pub const HeapCapsAllocator = struct {
|
|
caps: Caps = Caps.default_caps,
|
|
|
|
const Self = @This();
|
|
|
|
pub fn init(caps: ?Caps) Self {
|
|
return .{ .caps = caps orelse Caps.default_caps };
|
|
}
|
|
|
|
pub fn allocator(self: *Self) std.mem.Allocator {
|
|
return .{
|
|
.ptr = self,
|
|
.vtable = &.{
|
|
.alloc = alloc,
|
|
.resize = resize,
|
|
.remap = remap,
|
|
.free = free,
|
|
},
|
|
};
|
|
}
|
|
|
|
pub fn dump(self: Self) void {
|
|
sys.heap_caps_dump(self.caps.toRaw());
|
|
}
|
|
pub fn allocatedSize(_: Self, ptr: ?*anyopaque) usize {
|
|
return sys.heap_caps_get_allocated_size(ptr);
|
|
}
|
|
pub fn largestFreeBlock(self: Self) usize {
|
|
return sys.heap_caps_get_largest_free_block(self.caps.toRaw());
|
|
}
|
|
pub fn totalSize(self: Self) usize {
|
|
return sys.heap_caps_get_total_size(self.caps.toRaw());
|
|
}
|
|
pub fn freeSize(self: Self) usize {
|
|
return sys.heap_caps_get_free_size(self.caps.toRaw());
|
|
}
|
|
pub fn minimumFreeSize(self: Self) usize {
|
|
return sys.heap_caps_get_minimum_free_size(self.caps.toRaw());
|
|
}
|
|
pub fn internalFreeSize(_: Self) usize {
|
|
return sys.esp_get_free_internal_heap_size();
|
|
}
|
|
|
|
fn alloc(ctx: *anyopaque, len: usize, alignment: std.mem.Alignment, _: usize) ?[*]u8 {
|
|
const self: *Self = @ptrCast(@alignCast(ctx));
|
|
return @ptrCast(sys.heap_caps_aligned_alloc(
|
|
alignment.toByteUnits(),
|
|
len,
|
|
self.caps.toRaw(),
|
|
));
|
|
}
|
|
|
|
fn resize(_: *anyopaque, buf: []u8, _: std.mem.Alignment, new_len: usize, _: usize) bool {
|
|
if (new_len <= buf.len) return true;
|
|
if (@TypeOf(sys.heap_caps_get_allocated_size) != void) {
|
|
if (new_len <= sys.heap_caps_get_allocated_size(buf.ptr)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
fn remap(ctx: *anyopaque, memory: []u8, _: std.mem.Alignment, new_len: usize, _: usize) ?[*]u8 {
|
|
const self: *Self = @ptrCast(@alignCast(ctx));
|
|
return @ptrCast(sys.heap_caps_realloc(memory.ptr, new_len, self.caps.toRaw()));
|
|
}
|
|
|
|
fn free(_: *anyopaque, buf: []u8, _: std.mem.Alignment, _: usize) void {
|
|
sys.heap_caps_free(buf.ptr);
|
|
if (builtin.mode == .Debug) {
|
|
if (!sys.heap_caps_check_integrity_all(true))
|
|
@panic("heap_caps: integrity check failed after free");
|
|
}
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// MultiHeapAllocator
|
|
// ---------------------------------------------------------------------------
|
|
|
|
pub const MultiHeapAllocator = struct {
|
|
handle: sys.multi_heap_handle_t = null,
|
|
|
|
const Self = @This();
|
|
|
|
pub fn init(handle: sys.multi_heap_handle_t) Self {
|
|
return .{ .handle = handle };
|
|
}
|
|
|
|
pub fn allocator(self: *Self) std.mem.Allocator {
|
|
return .{
|
|
.ptr = self,
|
|
.vtable = &.{
|
|
.alloc = alloc,
|
|
.resize = resize,
|
|
.remap = remap,
|
|
.free = free,
|
|
},
|
|
};
|
|
}
|
|
|
|
pub fn allocatedSize(self: Self, p: ?*anyopaque) usize {
|
|
return sys.multi_heap_get_allocated_size(self.handle, p);
|
|
}
|
|
pub fn freeSize(self: Self) usize {
|
|
return sys.multi_heap_free_size(self.handle);
|
|
}
|
|
pub fn minimumFreeSize(self: Self) usize {
|
|
return sys.multi_heap_minimum_free_size(self.handle);
|
|
}
|
|
|
|
fn alloc(ctx: *anyopaque, len: usize, _: std.mem.Alignment, _: usize) ?[*]u8 {
|
|
const self: *Self = @ptrCast(@alignCast(ctx));
|
|
return @ptrCast(sys.multi_heap_malloc(self.handle, len));
|
|
}
|
|
|
|
fn resize(ctx: *anyopaque, buf: []u8, _: std.mem.Alignment, new_len: usize, _: usize) bool {
|
|
const self: *Self = @ptrCast(@alignCast(ctx));
|
|
if (new_len <= buf.len) return true;
|
|
if (@TypeOf(sys.multi_heap_get_allocated_size) != void) {
|
|
if (new_len <= sys.multi_heap_get_allocated_size(self.handle, buf.ptr))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
fn remap(ctx: *anyopaque, memory: []u8, _: std.mem.Alignment, new_len: usize, _: usize) ?[*]u8 {
|
|
const self: *Self = @ptrCast(@alignCast(ctx));
|
|
return @ptrCast(sys.multi_heap_realloc(self.handle, memory.ptr, new_len));
|
|
}
|
|
|
|
fn free(ctx: *anyopaque, buf: []u8, _: std.mem.Alignment, _: usize) void {
|
|
const self: *Self = @ptrCast(@alignCast(ctx));
|
|
sys.multi_heap_free(self.handle, buf.ptr);
|
|
if (builtin.mode == .Debug) {
|
|
if (!sys.multi_heap_check(self.handle, true))
|
|
@panic("multi_heap: integrity check failed after free");
|
|
}
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// VPortAllocator
|
|
// ---------------------------------------------------------------------------
|
|
|
|
pub const VPortAllocator = struct {
|
|
const Self = @This();
|
|
|
|
pub fn init() Self {
|
|
return .{};
|
|
}
|
|
|
|
pub fn allocator(self: *Self) std.mem.Allocator {
|
|
return .{
|
|
.ptr = self,
|
|
.vtable = &.{
|
|
.alloc = alloc,
|
|
.resize = resize,
|
|
.remap = remap,
|
|
.free = free,
|
|
},
|
|
};
|
|
}
|
|
|
|
pub fn freeSize(_: Self) usize {
|
|
return sys.xPortGetFreeHeapSize();
|
|
}
|
|
pub fn minimumFreeSize(_: Self) usize {
|
|
return sys.xPortGetMinimumEverFreeHeapSize();
|
|
}
|
|
|
|
fn alloc(_: *anyopaque, len: usize, _: std.mem.Alignment, _: usize) ?[*]u8 {
|
|
return @ptrCast(sys.pvPortMalloc(len));
|
|
}
|
|
|
|
fn resize(_: *anyopaque, buf: []u8, _: std.mem.Alignment, new_len: usize, _: usize) bool {
|
|
return new_len <= buf.len;
|
|
}
|
|
|
|
fn remap(_: *anyopaque, memory: []u8, _: std.mem.Alignment, new_len: usize, _: usize) ?[*]u8 {
|
|
const new_ptr = sys.pvPortMalloc(new_len) orelse return null;
|
|
@memcpy(@as([*]u8, @ptrCast(new_ptr))[0..@min(memory.len, new_len)], memory[0..@min(memory.len, new_len)]);
|
|
sys.vPortFree(memory.ptr);
|
|
return @ptrCast(new_ptr);
|
|
}
|
|
|
|
fn free(_: *anyopaque, buf: []u8, _: std.mem.Alignment, _: usize) void {
|
|
sys.vPortFree(buf.ptr);
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// TRACE
|
|
// ---------------------------------------------------------------------------
|
|
|
|
pub const TRACE = struct {
|
|
pub fn initStandalone(record_buffer: [*c]sys.heap_trace_record_t, num_records: usize) !void {
|
|
try errors.espCheckError(sys.heap_trace_init_standalone(record_buffer, num_records));
|
|
}
|
|
pub fn initTohost() !void {
|
|
try errors.espCheckError(sys.heap_trace_init_tohost());
|
|
}
|
|
pub fn start(mode: sys.heap_trace_mode_t) !void {
|
|
try errors.espCheckError(sys.heap_trace_start(mode));
|
|
}
|
|
pub fn stop() !void {
|
|
try errors.espCheckError(sys.heap_trace_stop());
|
|
}
|
|
pub fn @"resume"() !void {
|
|
try errors.espCheckError(sys.heap_trace_resume());
|
|
}
|
|
pub fn getCount() usize {
|
|
return sys.heap_trace_get_count();
|
|
}
|
|
pub fn get(index: usize, record: [*c]sys.heap_trace_record_t) !void {
|
|
try errors.espCheckError(sys.heap_trace_get(index, record));
|
|
}
|
|
pub fn dump() void {
|
|
sys.heap_trace_dump();
|
|
}
|
|
pub fn dumpCaps(caps: Caps) void {
|
|
sys.heap_trace_dump_caps(caps.toRaw());
|
|
}
|
|
pub fn summary(sum: [*c]sys.heap_trace_summary_t) !void {
|
|
try errors.espCheckError(sys.heap_trace_summary(sum));
|
|
}
|
|
};
|