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/RvMachineTimer.lvt

namespace Eccelerators.RiscV.Platform

using Eccelerators.RiscV.Sequential

/**
 * Platform-owned 64-bit time and deadline in the inherited clock/reset domain.
 * Four little-endian words at BASE: mtime low/high, mtimecmp low/high. Accepts one
 * aligned word request; byte masks apply to writes. Completion/data remain stable
 * until request falls. Errors precede effects; a held request never repeats a write.
 * Time advances every TICK_DIVISOR clocks even while the CPU is paused/restarted.
 * Hardware reset zeros time/divider and sets the comparison to all ones. A nonzero
 * mtime write suppresses that edge's complete 64-bit tick; divider phase continues.
 * Reads snapshot pre-edge state. Comparison writes and ticks may occur together.
 * timerPending is the live unsigned comparison, not an edge latch or acknowledge.
 */
component RvMachineTimer<BASE: uint = 0x40000100, TICK_DIVISOR: uint = 50,
	TICK_BITS: int = 6> : IRv32LoadStorePort
{
	static assert (BASE & 15) == 0 && BASE <= 0xfffffff0, "Timer base must fit four aligned words"
	static assert TICK_BITS > 0 && TICK_BITS <= 30, "Timer divider width must be 1..30"
	static assert TICK_DIVISOR > 0 && TICK_DIVISOR <= ((1 << TICK_BITS) as uint), "Timer divisor exceeds width"

	// Register offsets and lane expansion constants.
	public const TIME_LOW: uint = 0
	public const TIME_HIGH: uint = 4
	public const COMPARE_LOW: uint = 8
	public const COMPARE_HIGH: uint = 12
	const BYTE_MASK: uint = 255

	// Same-clock interrupt level, independent of CPU masking and monitor controls.
	public timerPending: out logic

	// Continuously updated timebase and inverted software deadline. Zero-reset bits
	// represent an all-ones deadline without constructor publication latency.
	timeLow: uint
	timeHigh: uint
	compareLowInverted: uint
	compareHighInverted: uint
	divider: logic[TICK_BITS]

	// Held completion and read snapshot for exactly-once MMIO.
	completed: bool
	responseError: logic
	responseData: logic[32]

	// Pure transaction decode and byte write merge, sampled by the single writer.
	validAddress: bool
	offset: uint
	mask: uint
	selected: uint
	merged: uint
	acceptWrite: bool

	/**
	 * Publishes registered completion and a live comparison without scheduled latency.
	 */
	process Decode[]()
	{
		ready = completed as logic
		error = responseError
		readData = responseData
		timerPending = (timeHigh > ~compareHighInverted
			|| (timeHigh == ~compareHighInverted && timeLow >= ~compareLowInverted)) as logic
		offset = (address as uint) - BASE
		validAddress = offset < 16 && (offset & 3) == 0
		mask = 0

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

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

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

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

		selected = 0

		match (offset)
		{
			TIME_LOW =>
			{
				selected = timeLow
			}
			TIME_HIGH =>
			{
				selected = timeHigh
			}
			COMPARE_LOW =>
			{
				selected = ~compareLowInverted
			}
			COMPARE_HIGH =>
			{
				selected = ~compareHighInverted
			}
			_ => {
				// Unmapped requests complete as errors without effects.
			}
		}

		merged = (selected & ~mask) | ((writeData as uint) & mask)
		acceptWrite = !completed && request == 0b1 && write == 0b1 && validAddress && mask != 0
	}

	/**
	 * Owns time, deadline and bus state; each accepted operation happens on one edge.
	 */
	process Advance()
	{
		state {
			if (divider as uint == TICK_DIVISOR - 1) {
				divider = 0

				if (!(acceptWrite && offset < COMPARE_LOW)) {
					if (timeLow == 0xffffffff) {
						timeHigh++
					}

					timeLow++
				}
			} else {
				divider = (divider as uint + 1) as logic[TICK_BITS]
			}

			if (!completed && request == 0b1) {
				completed = true
				responseError = (!validAddress) as logic
				responseData = selected as logic[32]

				if (acceptWrite) {
					match (offset)
					{
						TIME_LOW =>
						{
							timeLow = merged
						}
						TIME_HIGH =>
						{
							timeHigh = merged
						}
						COMPARE_LOW =>
						{
							compareLowInverted = ~merged
						}
						COMPARE_HIGH =>
						{
							compareHighInverted = ~merged
						}
						_ => {
							// validAddress excludes this branch.
						}
					}
				}
			} elif (completed && request == 0b0) {
				completed = false
			}
		}
	}
}