Q194FreeSystemVerilog
Allocate on a power-of-two boundary
Interview prompt
Question
Return a block whose starting address is aligned to a requested power-of-two boundary while preserving every unused fragment. Use the supplied constructor and free-list core. Allocation size must be positive; a zero size is a fatal input error. On an unsuccessful allocation, the returned address is unspecified.
Candidate starting point
Implementation scaffold
// Supplied free-list core.
typedef struct {
int unsigned lo;
int unsigned hi;
} range_t;
class RangeAllocator;
range_t free_ranges[$];
int unsigned managed_base;
int unsigned managed_limit;
function new(int unsigned base, int unsigned size);
if (size == 0) $fatal(1, "allocator size must be positive");
if (base > 32'hffff_ffff - (size - 1))
$fatal(1, "managed range wraps the address space");
managed_base = base;
managed_limit = base + size - 1;
free_ranges.push_back('{lo: managed_base, hi: managed_limit});
endfunction
function bit alloc(int unsigned size, output int unsigned addr);
if (size == 0) $fatal(1, "allocation size must be positive");
foreach (free_ranges[i]) begin
longint unsigned available =
longint'(free_ranges[i].hi) - free_ranges[i].lo + 1;
if (available >= size) begin
addr = free_ranges[i].lo;
if (available == size) free_ranges.delete(i);
else free_ranges[i].lo += size;
return 1;
end
end
return 0;
endfunction
function void dealloc(int unsigned addr, int unsigned size);
if (size == 0) $fatal(1, "deallocation size must be positive");
if (addr > 32'hffff_ffff - (size - 1))
$fatal(1, "deallocated range wraps the address space");
free_ranges.push_back('{lo: addr, hi: addr + size - 1});
coalesce();
endfunction
function void coalesce();
range_t merged[$];
range_t current;
if (free_ranges.size() <= 1) return;
free_ranges.sort() with (item.lo);
current = free_ranges[0];
for (int i = 1; i < free_ranges.size(); i++) begin
if (free_ranges[i].lo <= current.hi ||
(current.hi != 32'hffff_ffff &&
free_ranges[i].lo == current.hi + 1))
current.hi = (free_ranges[i].hi > current.hi) ?
free_ranges[i].hi : current.hi;
else begin
merged.push_back(current);
current = free_ranges[i];
end
end
merged.push_back(current);
free_ranges = merged;
endfunction
endclass
class AlignedRangeAllocator extends RangeAllocator;
function new(int unsigned base, int unsigned size);
super.new(base, size);
endfunction
function bit alloc_aligned(
int unsigned size,
int unsigned align,
output int unsigned addr
);
// Implement here: alloc_aligned.
endfunction
endclassReviewed example
Trace one case
Input
free=[[0x1003,0x10FF]]; size=0x20; alignment=0x40Expected output
base=0x1040; remaining=[[0x1003,0x103F],[0x1060,0x10FF]]Rounding 0x1003 upward selects the first 64-byte boundary, and both unused fragments remain available.
What to cover
Requirements
- Reject zero or non-power-of-two alignment.
- Round each candidate range start upward without signed arithmetic.
- Verify that the complete request fits after alignment.
- Preserve leading and trailing free fragments.
