Livt packages
Livt.IO
Version 1.8.1
Memory, UART, I2C, SPI, and Ethernet input/output components for Livt hardware designs.
Published files (126)
- ArrayRam.lvt
- AsynchronousDistributedRam.lvt
- AsynchronousDistributedRam32x16.lvt
- AsynchronousDistributedRam32x32.lvt
- AsynchronousDistributedRam8x64.lvt
- AsynchronousRam.lvt
- BlockRam.lvt
- DistributedRam.lvt
- IAsynchronousRam.lvt
- IRam.lvt
- ISynchronousRam.lvt
- Ram.lvt
- Ram16.lvt
- Ram32.lvt
- RamAccess.lvt
- RamGeometry.lvt
- SequentialRam.lvt
- SynchronousRam.lvt
src/memory/SequentialRam.lvt
namespace Livt.IO
/**
* Compact scheduled RAM requiring each operation to complete before the next begins.
* Read, Write and IsValidAddress must not overlap, including calls from different
* processes. There is no internal arbitration. Use Ram when reads
* and writes can be requested concurrently. Cells survive reset; unwritten cells
* are unspecified. Reset cancels a pending command and clears the read response.
* STYLE selects the portable synchronous backend's synthesis storage hint.
*/
component SequentialRam<T, CAPACITY: int = 64, STYLE: MemoryStyle = MemoryStyle.Block> : IRam<T>
{
static assert CAPACITY > 0, "RAM capacity must be positive"
const ADDRESS_BITS: int = Bits.RequiredFor(CAPACITY - 1)
// One owned port and one command mailbox; the client serializes both operations.
storage: SynchronousRam<T, logic[ADDRESS_BITS], CAPACITY, STYLE>
commandAddress: logic[ADDRESS_BITS]
payload: T
response: T
writeOperation: bool
request: bool
completed: bool
readInFlight: bool
/**
* Creates storage in the inherited clock/reset context.
*/
new()
{
storage = new SynchronousRam<T, logic[ADDRESS_BITS], CAPACITY, STYLE>()
}
/**
* Reports whether address selects a cell.
*/
override fn IsValidAddress(address: int) bool
{
return address >= 0 && address < CAPACITY
}
/**
* Reads one retained cell; invalid addresses return zero without a RAM command.
*/
override fn Read(address: int) T
{
if (address < 0 || address >= CAPACITY) { return 0 }
commandAddress = address as logic[ADDRESS_BITS]
writeOperation = false
request = !request
while (completed != request) {}
return response
}
/**
* Commits one write before returning; invalid addresses do not change cells.
*/
override fn Write(address: int, value: T)
{
if (address < 0 || address >= CAPACITY) { return }
commandAddress = address as logic[ADDRESS_BITS]
payload = value
writeOperation = true
request = !request
while (completed != request) {}
}
/**
* Holds one command at the memory port until its accepting edge.
*/
process Drive[]()
{
storage.enable = request != completed && !readInFlight
storage.writeEnable = writeOperation
storage.address = commandAddress
storage.writeData = payload
}
/**
* Completes writes at acceptance and reads after their one-edge response.
*/
process Complete()
{
state Tick {
if (readInFlight) {
response = storage.readData
completed = request
readInFlight = false
} elif (request != completed) {
if (writeOperation) { completed = request } else { readInFlight = true }
}
goto Tick
}
}
}