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/Platform/RvExternalInterrupts.lvt

namespace Eccelerators.RiscV.Platform

using Eccelerators.RiscV.Sequential

/**
 * Platform-owned level aggregator in the inherited clock/reset domain. Inputs
 * must already be synchronous and remain asserted until their device is serviced.
 * BASE+0 reads raw pending, +4 reads/writes enables, +8 reads enabled pending.
 * Read-only or invalid writes fail before effects. Byte masks apply to enables.
 * A read snapshots pre-edge state; completion stays held until request falls.
 * Reset clears enables/response, never acknowledges devices. CPU pause/restart
 * does not change state. Firmware chooses source service order from the bitmap.
 */
component RvExternalInterrupts<SOURCES: int = 2, BASE: uint = 0x40000200> : IRv32LoadStorePort
{
	static assert SOURCES >= 1 && SOURCES <= 32, "Source count must be 1..32"
	static assert (BASE & 15) == 0 && BASE <= 0xfffffff0, "Interrupt base must fit aligned window"

	// Register offsets and supported bits.
	public const PENDING: uint = 0
	public const ENABLE: uint = 4
	public const ACTIVE: uint = 8
	const SOURCE_MASK: uint = 0xffffffff >> (32 - SOURCES)

	// Synchronous device levels and combined enabled interrupt output.
	public sources: in logic[SOURCES]
	public externalPending: out logic

	// Single-writer enable and held transaction state.
	enabled: uint
	completed: bool
	responseError: logic
	responseData: logic[32]

	// Pure request view and write-lane expansion.
	offset: uint
	validAccess: bool
	mask: uint
	selected: uint

	/**
	 * Exposes live levels independently of CPU masks and a held read snapshot.
	 */
	process Decode[]()
	{
		ready = completed as logic
		error = responseError
		readData = responseData
		externalPending = (((sources as uint) & enabled) != 0) as logic
		offset = (address as uint) - BASE
		validAccess = (offset == PENDING || offset == ENABLE || offset == ACTIVE)
			&& (write == 0b0 || offset == ENABLE)
		mask = 0

		if (((byteEnable as uint) & 1) != 0) {
			mask = mask | ((255 as uint) << 0)
		}

		if (((byteEnable as uint) & 2) != 0) {
			mask = mask | ((255 as uint) << 8)
		}

		if (((byteEnable as uint) & 4) != 0) {
			mask = mask | ((255 as uint) << 16)
		}

		if (((byteEnable as uint) & 8) != 0) {
			mask = mask | ((255 as uint) << 24)
		}

		selected = 0

		match (offset)
		{
			PENDING =>
			{
				selected = sources as uint
			}
			ENABLE =>
			{
				selected = enabled
			}
			ACTIVE =>
			{
				selected = (sources as uint) & enabled
			}
			_ =>
			{
			}
		}
	}

	/**
	 * Commits each enable update once; accepting an interrupt has no device effects.
	 */
	process Advance()
	{
		state {
			if (!completed && request == 0b1) {
				completed = true
				responseError = (!validAccess) as logic
				responseData = selected as logic[32]

				if (validAccess && write == 0b1) {
					enabled = ((enabled & ~mask) | ((writeData as uint) & mask)) & SOURCE_MASK
				}
			} elif (completed && request == 0b0) {
				completed = false
			}
		}
	}
}