Livt packages

Eccelerators.RiscV

Version 0.3.0

Composable RV32I and Zmmul processors, machine-mode support, and reusable FPGA system components written in Livt.

Published files (120)

src/Storage/RvFetchRom.lvt

namespace Eccelerators.RiscV.Storage

using Eccelerators.RiscV.Sequential
using Livt.IO

/**
 * Exclusive, synchronous read-only instruction ROM with one outstanding word
 * transaction. Reads are side-effect free. Out-of-range or unaligned requests
 * fault before a RAM access. Reset retains payload; initialize every location
 * read. The composition root may provision whole words only while the CPU is
 * idle and request is low. Programming and execution must never overlap.
 */
component RvFetchRom<WORDS: int = 1024, ADDRESS_BITS: int = 10,
	STYLE: MemoryStyle = MemoryStyle.Block, BASE: uint = 0> : IRv32FetchPort
{
	static assert (BASE & 3) == 0, "Memory base must be word aligned"
	static assert ADDRESS_BITS > 0 && ADDRESS_BITS <= 28, "Memory address width must be 1..28"
	static assert WORDS > 0 && WORDS <= (1 << ADDRESS_BITS), "Memory capacity exceeds address width"

	static assert BASE <= 0xffffffff - ((WORDS as uint) * 4 - 1), "Memory range must not wrap"

	// Host provisioning signals, sampled on an enabled edge with an idle fetch.
	public programWrite: in logic
	public programIndex: in logic[ADDRESS_BITS]
	public programData: in logic[32]

	// Transaction phases.
	const IDLE: uint = 0
	const CAPTURE: uint = 1
	const RESPONSE: uint = 2

	// Owned native synchronous memory and transaction phase; no payload reset loop.
	storage: SynchronousRam<logic[32], logic[ADDRESS_BITS], WORDS, STYLE>
	phase: uint
	responseError: logic
	responseData: logic[32]

	// Combinational address validity.
	validAddress: bool

	/**
	 * Creates exactly one synchronous payload memory.
	 */
	new()
	{
		storage = new SynchronousRam<logic[32], logic[ADDRESS_BITS], WORDS, STYLE>()
	}

	/**
	 * Maps fetch or provisioning operations onto the sole RAM port.
	 */
	process Drive[]()
	{
		validAddress = (address as uint & 3) == 0 && ((address as uint - BASE) >> 2) < (WORDS as uint)
		error = responseError
		instruction = responseData
		ready = 0b0

		if (phase == RESPONSE) {
			ready = 0b1
		}

		storage.enable = false
		storage.writeEnable = false
		storage.address = ((address as uint - BASE) >> 2) as logic[ADDRESS_BITS]
		storage.writeData = 0

		if (phase == IDLE && request == 0b1 && validAddress) {
			storage.enable = true
		} elif (phase == IDLE && request == 0b0 && programWrite == 0b1) {
			storage.enable = true
			storage.writeEnable = true
			storage.address = programIndex
			storage.writeData = programData
		}
	}

	/**
	 * Completes each fetch request once, including errors.
	 */
	process Advance()
	{
		state {
			match (phase)
			{
				IDLE =>
				{
					if (request == 0b1) {
						responseError = 0b0

						if (!validAddress) {
							responseError = 0b1
							phase = RESPONSE
						} else {
							phase = CAPTURE
						}
					}
				}
				CAPTURE =>
				{
					responseData = storage.readData
					phase = RESPONSE
				}
				RESPONSE =>
				{
					if (request == 0b0) {
						phase = IDLE
					}
				}
				_ =>
				{
					phase = IDLE
				}
			}
		}
	}
}