Livt packages

Livt.IO

Version 1.8.1

Memory, UART, I2C, SPI, and Ethernet input/output components for Livt hardware designs.

Published files (126)

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
		}
	}
}