Livt packages
Livt.FileSystems
Version 0.0.2
Bounded, read-only FAT32 file streaming and partition discovery for Livt FPGA applications.
Published files (15)
src/SectorBuffer.lvt
namespace Livt.FileSystems
using Livt.IO
/**
* Owns configurable 512-byte storage and borrows the block device's only client.
* BRAM is the default; any serialized IRam provider with at least 512 cells works.
* Provider latency is preserved. The underlying device retains its sector until
* copying completes. Service also invalidates copied bytes when media disappears.
*/
component SectorBuffer<STORAGE: IRam<byte> & new() = SequentialRam<byte, 512, MemoryStyle.Block>> : ISectorBuffer
{
// Borrowed exclusive block-device client and owned sector memory.
device: IReadOnlyBlockDevice
memory: STORAGE
// Copy cursor and cancellation state, independent of payload RAM contents.
index: int
cancelling: bool
/**
* Binds a device in the same clock/reset context and allocates one sector.
*/
new(device: IReadOnlyBlockDevice)
{
this.device = device
memory = new STORAGE()
}
/**
* Accepts an LBA only from a ready, idle device with 512-byte blocks.
* True reserves the read and invalidates old content; call Service to complete.
* False preserves this buffer state and does not acquire device ownership.
*/
override fn Start(lba: uint) bool
{
if (busy || !memory.IsValidAddress(511) || !device.IsReady()
|| device.GetBlockSize() != 512 || !device.TryReadBlock(lba)) {
return false
}
busy = true
valid = false
failed = false
cancelling = false
index = 0
return true
}
/**
* Copies one byte or releases a terminal device completion.
*/
override fn Service()
{
if (!device.IsReady()) {
valid = false
failed = true
}
if (!busy) {
return
}
var status = device.GetReadState()
if (status == BlockReadState.Active) {
return
}
if (!cancelling && !failed && status == BlockReadState.Complete) {
var value: byte
if (device.TryReadByte(index, value)) {
memory.Write(index, value)
index++
if (index < 512) {
return
}
valid = true
} else {
failed = true
}
} else {
failed = true
}
device.ReleaseRead()
busy = false
}
/**
* Invalidates bytes immediately; callers keep servicing until busy is false.
*/
override fn Cancel()
{
valid = false
cancelling = true
if (busy) {
device.CancelRead()
}
}
/**
* Returns zero for invalid content or offsets.
*/
override fn ReadByte(offset: int) byte
{
if (!valid || offset < 0 || offset >= 512) {
return 0
}
return memory.Read(offset)
}
/**
* Reads a little-endian word through scheduled storage calls.
* Requires valid content and offset 0..510; calls must remain serialized.
*/
override fn Read16(offset: int) uint
{
var result = ReadByte(offset) as uint
return result | ((ReadByte(offset + 1) as uint) << 8)
}
/**
* Reads a little-endian double word through scheduled storage calls.
* Requires valid content and offset 0..508; calls must remain serialized.
*/
override fn Read32(offset: int) uint
{
var result = Read16(offset)
return result | (Read16(offset + 2) << 16)
}
}