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/wishbone/testing/WishboneTestSlave.lvt

namespace Livt.Bus.Wishbone.Testing

using Livt.Bus.Wishbone

/**
 * Configurable Wishbone Classic mirrored 16-word register model for master tests.
 */
component WishboneTestSlave<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: IWishboneMaster<Logic[AddressWidth], Logic[DataWidth], Logic[BYTE_COUNT]>
	registers: Logic[16, DataWidth]
	ackDelay: uint

	new(bus: flip IWishboneMaster<Logic[AddressWidth], Logic[DataWidth], Logic[BYTE_COUNT]>)
	{
		this.bus = bus
		this.bus.datout = 0x00000000
		this.bus.ack = 0b0
		this.ackDelay = 0
	}

	/**
	 * Sets the number of clock ticks to wait before asserting ack.
	 */
	public fn ConfigureAckDelay(ackDelay: uint)
	{
		state Apply {
			this.ackDelay = ackDelay
		}
	}

	/**
	 * 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.ack = 0b0
			elapsed = 0

			if (this.bus.cyc == 0b1 && this.bus.stb == 0b1) {
				goto WaitForAck
			} else {
				goto Idle
			}
		}

		state WaitForAck {
			if (this.bus.cyc == 0b0 || this.bus.stb == 0b0) {
				this.bus.ack = 0b0
				goto Idle
			} elif (elapsed >= ackDelay) {
				this.observedAddress = this.bus.adr
				registerIndex = (((this.bus.adr & ((16 * BYTE_COUNT - 1) as Logic[AddressWidth])) as uint) / (BYTE_COUNT as uint)) as int

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

				if (this.bus.we == 0b0) {
					if (registerIndex >= 0 && registerIndex < 16) {
						this.bus.datout = registers[registerIndex] as Logic[DataWidth]
					} else {
						this.bus.datout = 0x00000000
					}
				}

				this.bus.ack = 0b1
				goto Acknowledge
			} else {
				elapsed = elapsed + 1
				goto WaitForAck
			}
		}

		state Acknowledge {
			// Classic cycles complete once; do not accept the held request again.
			if (this.bus.cyc == 0b0 || this.bus.stb == 0b0) {
				this.bus.ack = 0b0
				goto Idle
			} else {
				goto Acknowledge
			}
		}
	}

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