Livt packages
Eccelerators.Cli
Version 1.4.1
Transport-independent, bounded command-line input, parsing, editing, and output for Livt FPGA applications.
Published files (23)
src/CliOutput.lvt
namespace Eccelerators.Cli
using Livt.IO
/**
* Bounded byte queue used for CLI echo, responses, errors, and prompts.
* Owns B with at least 64 byte addresses in the inherited clock/reset context.
* One caller serializes operations; only written, logically valid cells are read.
* Clear discards metadata without requiring payload reset. Calls may take multiple cycles.
* Defaults to block RAM; consumers may select another compatible IRam backend.
*/
component CliOutput<B: IRam<byte> & new() = Ram<byte, 64, MemoryStyle.Block>>
{
// Number of bytes available in the circular output queue.
public const BUFFER_SIZE: int = 64
// Right-sized scheduled storage; live queue entries are always written first.
buffer: B
// Circular queue indices and number of initialized, readable entries.
head: byte
tail: byte
count: byte
/**
* Creates the byte store without relying on unwritten cell contents.
*/
new()
{
this.buffer = new B()
}
/**
* Returns the number of queued output bytes.
*/
public fn GetCount() int
{
return this.count as int
}
/**
* Returns the remaining output capacity.
*/
public fn GetFreeSpace() int
{
return BUFFER_SIZE - (this.count as int)
}
/**
* Returns true when at least one output byte is queued.
*/
public fn HasData() bool
{
return this.count != 0
}
/**
* Returns true when an atomic write of length bytes can be accepted.
*/
public fn CanWrite(length: int) bool
{
if (length < 0) { return false }
return length <= this.GetFreeSpace()
}
/**
* Queues one byte, returning false without changing the queue when full.
*/
public fn TryWriteByte(value: byte) bool
{
if ((this.count as int) >= BUFFER_SIZE) { return false }
var tailIndex: int = this.tail as int
this.buffer.Write(tailIndex, value)
tailIndex = tailIndex + 1
if (tailIndex == BUFFER_SIZE) { tailIndex = 0 }
this.tail = tailIndex as byte
this.count = ((this.count as int) + 1) as byte
return true
}
/**
* Queues the complete array atomically.
*/
public fn TryWrite(data: byte[]) bool
{
var length: int = data.Length()
if (length > this.GetFreeSpace()) { return false }
var tailIndex: int = this.tail as int
var nextCount: int = this.count as int
for (var i = 0; i < length; i++) {
this.buffer.Write(tailIndex, data[i])
tailIndex = tailIndex + 1
if (tailIndex == BUFFER_SIZE) { tailIndex = 0 }
nextCount = nextCount + 1
}
this.tail = tailIndex as byte
this.count = nextCount as byte
return true
}
/**
* Queues the complete array followed by CRLF atomically.
*/
public fn TryWriteLine(data: byte[]) bool
{
var length: int = data.Length()
if (length + 2 > this.GetFreeSpace()) { return false }
var tailIndex: int = this.tail as int
var nextCount: int = this.count as int
for (var i = 0; i < length; i++) {
this.buffer.Write(tailIndex, data[i])
tailIndex = tailIndex + 1
if (tailIndex == BUFFER_SIZE) { tailIndex = 0 }
nextCount = nextCount + 1
}
this.buffer.Write(tailIndex, 0x0D)
tailIndex = tailIndex + 1
if (tailIndex == BUFFER_SIZE) { tailIndex = 0 }
nextCount = nextCount + 1
this.buffer.Write(tailIndex, 0x0A)
tailIndex = tailIndex + 1
if (tailIndex == BUFFER_SIZE) { tailIndex = 0 }
nextCount = nextCount + 1
this.tail = tailIndex as byte
this.count = nextCount as byte
return true
}
/**
* Returns the oldest queued byte without removing it.
*/
public fn Peek() byte
{
if (this.count == 0) { return 0x00 }
return this.buffer.Read(this.head as int)
}
/**
* Removes the oldest queued byte and reports whether one was present.
*/
public fn Consume() bool
{
if (this.count == 0) { return false }
var headIndex: int = (this.head as int) + 1
if (headIndex == BUFFER_SIZE) { headIndex = 0 }
this.head = headIndex as byte
this.count = ((this.count as int) - 1) as byte
return true
}
/**
* Removes all queued output.
*/
public fn Clear()
{
this.head = 0
this.tail = 0
this.count = 0
}
}