Livt packages

Livt.Bus

Version 2.0.1

Configurable Avalon, AXI4-Lite, and Wishbone memory-mapped masters, interfaces, bridges, and test components.

Published files (67)

src/avalon/testing/AvalonTestSlave.lvt

namespace Livt.Bus.Avalon.Testing

using Livt.Bus.Avalon

/**
 * Configurable non-pipelined Avalon-MM mirrored 16-word register model for master tests.
 */
component AvalonTestSlave<AddressWidth: int = 32, DataWidth: int = 32>
{
	const BYTE_COUNT: int = DataWidth / 8
static assert DataWidth == 32 || DataWidth == 64 || DataWidth == 128, "Invalid DataWidth"

	public observedAddress: out Logic[AddressWidth]
	public acceptedWrites: out uint

	bus: IAvalonMaster<Logic[AddressWidth], Logic[DataWidth], Logic[BYTE_COUNT]>
	registers: Logic[16, DataWidth]
	requestDelay: uint
	configuredResponse: Logic[2]

	new(bus: flip IAvalonMaster<Logic[AddressWidth], Logic[DataWidth], Logic[BYTE_COUNT]>)
	{
		this.bus = bus
		this.bus.readdata = 0x00000000
		this.bus.waitrequest = 0b1
		this.bus.response = 0b00
		this.requestDelay = 0
		this.configuredResponse = 0b00
	}

	/**
	 * Sets the request delay in clock ticks and the response returned on acceptance.
	 */
	public fn Configure(requestDelay: uint, response: Logic[2])
	{
		state Apply {
			this.requestDelay = requestDelay
			this.configuredResponse = response
		}
	}

	/**
	 * Clears all sixteen native-width words in the target model.
	 */
	public fn Clear()
	{
		var index: int = 0

		while (index < 16) {
			registers[index] = 0
			index = index + 1
		}
	}

	process Run()
	{
		var elapsed: uint = 0
		var registerIndex: int = 0
		var nextValue: Logic[DataWidth] = 0

		state Idle {
			this.bus.waitrequest = 0b1
			elapsed = 0

			if (this.bus.write == 0b1 || this.bus.read == 0b1) {
				goto WaitForAcceptance
			} else {
				goto Idle
			}
		}

		state WaitForAcceptance {
			if (elapsed >= requestDelay) {
				this.observedAddress = this.bus.address
				registerIndex = (((this.bus.address & ((16 * BYTE_COUNT - 1) as Logic[AddressWidth])) as uint) / (BYTE_COUNT as uint)) as int

				if (this.bus.write == 0b1 && registerIndex >= 0 && registerIndex < 16) {
					nextValue = registers[registerIndex]
					nextValue = this.MergeBytes(nextValue, this.bus.writedata, this.bus.byteenable)
					this.acceptedWrites = this.acceptedWrites + 1
					registers[registerIndex] = nextValue
				}

				if (this.bus.read == 0b1) {
					if (registerIndex >= 0 && registerIndex < 16) {
						this.bus.readdata = registers[registerIndex] as Logic[DataWidth]
					} else {
						this.bus.readdata = 0x00000000
					}
				}

				this.bus.response = configuredResponse
				this.bus.waitrequest = 0b0
				goto Accepted
			} else {
				elapsed = elapsed + 1
				goto WaitForAcceptance
			}
		}

		state Accepted {
			// This fixture models a single outstanding transaction. A delegated
			// request can remain asserted while its response travels upstream.
			if (this.bus.read == 0b0 && this.bus.write == 0b0) {
				this.bus.waitrequest = 0b1
				goto Idle
			} else {
				goto Accepted
			}
		}
	}

	private inline fn MergeBytes[](previous: Logic[DataWidth], data: Logic[DataWidth], enables: Logic[BYTE_COUNT]) Logic[DataWidth]
	{
		var result = previous

		for (var lane = 0; lane < 16; lane++) {
			if (lane < BYTE_COUNT) {
				if (enables[lane] == 0b1) {
					var mask = (0xFF as Logic[DataWidth]) << (lane * 8)
					result = (result & ~mask) | (data & mask)
				}
			}
		}

		return result
	}
}