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/axi4lite/testing/Axi4LiteTestSlave.lvt

namespace Livt.Bus.Axi4Lite.Testing

using Livt.Bus.Axi4Lite

/**
 * Configurable AXI4-Lite slave used to verify the public test master.
 */
component Axi4LiteTestSlave<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: IAxi4LiteMaster<Logic[AddressWidth], Logic[DataWidth], Logic[BYTE_COUNT]>
	registers: Logic[16, DataWidth]
	awDelay: uint
	wDelay: uint
	bDelay: uint
	arDelay: uint
	rDelay: uint
	configuredWriteResponse: Logic[2]
	configuredReadResponse: Logic[2]

	new(bus: flip IAxi4LiteMaster<Logic[AddressWidth], Logic[DataWidth], Logic[BYTE_COUNT]>)
	{
		this.bus = bus
		this.bus.awready = 0b0
		this.bus.wready = 0b0
		this.bus.bvalid = 0b0
		this.bus.bresp = 0b00
		this.bus.arready = 0b0
		this.bus.rvalid = 0b0
		this.bus.rdata = 0x00000000
		this.bus.rresp = 0b00
		this.awDelay = 0
		this.wDelay = 0
		this.bDelay = 0
		this.arDelay = 0
		this.rDelay = 0
		this.configuredWriteResponse = 0b00
		this.configuredReadResponse = 0b00
	}

	/**
	 * Configures clock-tick delays for write address, write data, write response,
	 * read address, and read data handshakes respectively.
	 */
	public fn ConfigureDelays(awDelay: uint, wDelay: uint, bDelay: uint, arDelay: uint, rDelay: uint)
	{
		state Apply {
			this.awDelay = awDelay
			this.wDelay = wDelay
			this.bDelay = bDelay
			this.arDelay = arDelay
			this.rDelay = rDelay
		}
	}

	/**
	 * Configures the two-bit AXI response returned by subsequent writes and reads.
	 */
	public fn ConfigureResponses(writeResponse: Logic[2], readResponse: Logic[2])
	{
		state Apply {
			this.configuredWriteResponse = writeResponse
			this.configuredReadResponse = readResponse
		}
	}

	/**
	 * 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 awTicks: uint = 0
		var wTicks: uint = 0
		var bTicks: uint = 0
		var arTicks: uint = 0
		var rTicks: uint = 0
		var addressAccepted: bool = false
		var dataAccepted: bool = false
		var capturedWriteAddress: Logic[AddressWidth] = 0
		var capturedWriteData: Logic[DataWidth] = 0
		var capturedWriteStrobe: Logic[BYTE_COUNT] = 0b0000
		var capturedReadAddress: Logic[AddressWidth] = 0
		var registerIndex: int = 0
		var nextValue: Logic[DataWidth] = 0

		state Idle {
			this.bus.awready = 0b0
			this.bus.wready = 0b0
			this.bus.bvalid = 0b0
			this.bus.arready = 0b0
			this.bus.rvalid = 0b0
			addressAccepted = false
			dataAccepted = false
			awTicks = 0
			wTicks = 0
			bTicks = 0
			arTicks = 0
			rTicks = 0

			if (this.bus.awvalid == 0b1 || this.bus.wvalid == 0b1) {
				goto WriteTransfer
			} elif (this.bus.arvalid == 0b1) {
				goto ReadAddress
			} else {
				goto Idle
			}
		}

		state WriteTransfer {
			if (addressAccepted == false) {
				if (awTicks >= awDelay) {
					if (this.bus.awready == 0b1 && this.bus.awvalid == 0b1) {
						capturedWriteAddress = this.bus.awaddr as Logic[AddressWidth]
						addressAccepted = true
						this.bus.awready = 0b0
					} else {
						this.bus.awready = 0b1
					}
				} else {
					awTicks = awTicks + 1
				}
			}

			if (dataAccepted == false) {
				if (wTicks >= wDelay) {
					if (this.bus.wready == 0b1 && this.bus.wvalid == 0b1) {
						capturedWriteData = this.bus.wdata as Logic[DataWidth]
						capturedWriteStrobe = this.bus.wstrb
						dataAccepted = true
						this.bus.wready = 0b0
					} else {
						this.bus.wready = 0b1
					}
				} else {
					wTicks = wTicks + 1
				}
			}

			if (addressAccepted && dataAccepted) {
				goto WriteApply
			} else {
				goto WriteTransfer
			}
		}

		state WriteApply {
			this.bus.awready = 0b0
			this.bus.wready = 0b0
			this.observedAddress = capturedWriteAddress
			registerIndex = (((capturedWriteAddress & ((16 * BYTE_COUNT - 1) as Logic[AddressWidth])) as uint) / (BYTE_COUNT as uint)) as int

			if (registerIndex >= 0 && registerIndex < 16) {
				nextValue = registers[registerIndex]
				nextValue = this.MergeBytes(nextValue, capturedWriteData, capturedWriteStrobe)
				this.acceptedWrites = this.acceptedWrites + 1
				registers[registerIndex] = nextValue
			}

			bTicks = 0
			goto WriteResponseDelay
		}

		state WriteResponseDelay {
			if (bTicks >= bDelay) {
				this.bus.bresp = configuredWriteResponse
				this.bus.bvalid = 0b1
				goto WriteResponse
			} else {
				bTicks = bTicks + 1
				goto WriteResponseDelay
			}
		}

		state WriteResponse {
			if (this.bus.bready == 0b1) {
				this.bus.bvalid = 0b0
				goto Idle
			} else {
				goto WriteResponse
			}
		}

		state ReadAddress {
			if (arTicks >= arDelay) {
				this.bus.arready = 0b1

				if (this.bus.arvalid == 0b1) {
					capturedReadAddress = this.bus.araddr as Logic[AddressWidth]
					rTicks = 0
					goto ReadDataDelay
				} else {
					goto ReadAddress
				}
			} else {
				arTicks = arTicks + 1
				goto ReadAddress
			}
		}

		state ReadDataDelay {
			this.bus.arready = 0b0

			if (rTicks >= rDelay) {
				this.observedAddress = capturedReadAddress
				registerIndex = (((capturedReadAddress & ((16 * BYTE_COUNT - 1) as Logic[AddressWidth])) as uint) / (BYTE_COUNT as uint)) as int

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

				this.bus.rresp = configuredReadResponse
				this.bus.rvalid = 0b1
				goto ReadData
			} else {
				rTicks = rTicks + 1
				goto ReadDataDelay
			}
		}

		state ReadData {
			if (this.bus.rready == 0b1) {
				this.bus.rvalid = 0b0
				goto Idle
			} else {
				goto ReadData
			}
		}
	}

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