Livt packages

Eccelerators.Video

Version 1.0.1

Streaming pixel pipelines, filters, test patterns, and parallel camera capture for Livt FPGA applications.

Published files (22)

src/PatternSource.lvt

namespace Eccelerators.Video

/**
 * Owns raster counters; emits Gray8 ramp/checker or RGB565 color-bar pixels at up to one per edge.
 * Borrows stable enable/pattern controls. Change controls only with reset or at
 * a frame boundary. LINE_BLANK inserts invalid cycles after each accepted line.
 * Stalls preserve the entire pixel token. Context and reset are inherited.
 */
component PatternSource<WIDTH: int = 320, HEIGHT: int = 240, LINE_BLANK: int = 0> : IPixelStream
{
	static assert WIDTH > 0 && WIDTH <= 4096, "Supported width is 1..4096"
	static assert HEIGHT > 0 && HEIGHT <= 4096, "Supported height is 1..4096"
	static assert LINE_BLANK >= 0, "Blanking cannot be negative"

	// Borrowed stable control signals.
	enabled: bool
	pattern: int

	// Owned raster position, blanking counter and wrapping frame identity.
	x: int
	y: int
	blank: int
	sequence: uint

	/**
	 * Binds pattern (0 ramp, 1 checker, 2 RGB565 bars) and starts at pixel (0,0), frame zero after reset.
	 */
	new(enabled: bool, pattern: int)
	{
		this.enabled = enabled
		this.pattern = pattern
	}

	/**
	 * Produces a token combinationally; no registered accessor or call scheduler.
	 */
	process Signals[]()
	{
		this.valid = this.enabled && this.blank == 0
		this.data = ((this.x + this.y * WIDTH) % 256) as logic[16]

		if (this.pattern == 1) {
			if ((this.x / 8 + this.y / 8) % 2 == 0) {
				this.data = 0
			} else {
				this.data = 255
			}
		}

		if (this.pattern == 2) {
			if (this.x < (1 * WIDTH + 7) / 8) {
				this.data = 0xFFFF
			} elif (this.x < (2 * WIDTH + 7) / 8) {
				this.data = 0xFFE0
			} elif (this.x < (3 * WIDTH + 7) / 8) {
				this.data = 0x07FF
			} elif (this.x < (4 * WIDTH + 7) / 8) {
				this.data = 0x07E0
			} elif (this.x < (5 * WIDTH + 7) / 8) {
				this.data = 0xF81F
			} elif (this.x < (6 * WIDTH + 7) / 8) {
				this.data = 0xF800
			} elif (this.x < (7 * WIDTH + 7) / 8) {
				this.data = 0x001F
			} else {
				this.data = 0
			}
		}

		this.frameStart = this.x == 0 && this.y == 0
		this.lineEnd = this.x == WIDTH - 1
		this.frameEnd = this.x == WIDTH - 1 && this.y == HEIGHT - 1
		this.frameId = this.sequence
	}

	/**
	 * Advances only on acceptance; each iteration is one context edge.
	 */
	process Raster()
	{
		state Tick {
			if (this.blank > 0) {
				this.blank--
			} elif (this.valid && this.ready) {
				if (this.x == WIDTH - 1) {
					this.x = 0
					this.blank = LINE_BLANK

					if (this.y == HEIGHT - 1) {
						this.y = 0
						this.sequence++
					} else {
						this.y++
					}
				} else {
					this.x++
				}
			}

			goto Tick
		}
	}
}