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/Window3x3.lvt
namespace Eccelerators.Video
using Livt.IO
/**
* Gray8 3x3 window with explicit synchronous block-RAM line storage.
* Input must be a valid WIDTH by HEIGHT raster with coherent markers. Crops one
* pixel at every border: output dimensions are WIDTH-2 by HEIGHT-2.
* One read edge and one write edge per accepted input pixel: maximum one pixel
* per two context edges. Backpressure propagates; live cameras require a FIFO.
* RAM is not reset; the first two rows initialize it before any window is valid.
*/
component Window3x3<WIDTH: int = 320, HEIGHT: int = 240>
{
static assert WIDTH >= 3 && WIDTH <= 4096 && HEIGHT >= 3, "Window requires at least 3x3"
// Explicit line storage: WIDTH bytes each, one synchronous port, block mapping requested.
previousLine: SynchronousRam<byte, logic[12], WIDTH, MemoryStyle.Block>
// Second previous row uses an independent block-RAM port in the same clock domain.
olderLine: SynchronousRam<byte, logic[12], WIDTH, MemoryStyle.Block>
// Input Gray8 token; valid/ready acceptance and markers are sampled together.
public inputData: in byte
public inputValid: in bool
public inputStart: in bool
public inputLineEnd: in bool
public inputEnd: in bool
public inputId: in uint
public upstreamReady: out bool
// Output row-major window and cropped-raster metadata; held during stalls.
public topLeft: out byte
public topCenter: out byte
public topRight: out byte
public middleLeft: out byte
public middleCenter: out byte
public middleRight: out byte
public bottomLeft: out byte
public bottomCenter: out byte
public bottomRight: out byte
public valid: out bool
public ready: in bool
public frameStart: out bool
public lineEnd: out bool
public frameEnd: out bool
public frameId: out uint
// Owned RAM phase, raster position, and accepted pixel held across read latency.
writePhase: bool
x: int
y: int
pixel: byte
identity: uint
// Two previous columns of each row, valid only after the first two columns.
topOlder: byte
topPrevious: byte
middleOlder: byte
middlePrevious: byte
bottomOlder: byte
bottomPrevious: byte
// Owned output window registers; comb exports preserve uniform latency.
heldTopLeft: byte
heldTopCenter: byte
heldTopRight: byte
heldMiddleLeft: byte
heldMiddleCenter: byte
heldMiddleRight: byte
heldBottomLeft: byte
heldBottomCenter: byte
heldBottomRight: byte
heldValid: bool
heldStart: bool
heldLineEnd: bool
heldEnd: bool
heldId: uint
/**
* Owns two independent memories, retaining cells across reset.
*/
new()
{
this.previousLine = new SynchronousRam<byte, logic[12], WIDTH, MemoryStyle.Block>()
this.olderLine = new SynchronousRam<byte, logic[12], WIDTH, MemoryStyle.Block>()
}
/**
* Both memories read a column together, then replace it together one edge later.
*/
process Wire[]()
{
this.upstreamReady = !this.writePhase && (!this.heldValid || this.ready)
var address = this.x
if (!this.writePhase && this.inputStart) {
address = 0
}
this.previousLine.address = address as logic[12]
this.olderLine.address = address as logic[12]
this.previousLine.enable = this.writePhase || (this.inputValid && this.upstreamReady)
this.olderLine.enable = this.previousLine.enable
this.previousLine.writeEnable = this.writePhase
this.olderLine.writeEnable = this.writePhase
this.previousLine.writeData = this.pixel
this.olderLine.writeData = this.previousLine.readData
this.topLeft = this.heldTopLeft
this.topCenter = this.heldTopCenter
this.topRight = this.heldTopRight
this.middleLeft = this.heldMiddleLeft
this.middleCenter = this.heldMiddleCenter
this.middleRight = this.heldMiddleRight
this.bottomLeft = this.heldBottomLeft
this.bottomCenter = this.heldBottomCenter
this.bottomRight = this.heldBottomRight
this.valid = this.heldValid
this.frameStart = this.heldStart
this.lineEnd = this.heldLineEnd
this.frameEnd = this.heldEnd
this.frameId = this.heldId
}
/**
* Consumes the synchronous read and advances only on committed input pixels.
*/
process Raster()
{
state Tick {
if (this.ready) {
this.heldValid = false
}
if (this.writePhase) {
if (this.x >= 2 && this.y >= 2) {
this.heldTopLeft = this.topOlder
this.heldTopCenter = this.topPrevious
this.heldTopRight = this.olderLine.readData
this.heldMiddleLeft = this.middleOlder
this.heldMiddleCenter = this.middlePrevious
this.heldMiddleRight = this.previousLine.readData
this.heldBottomLeft = this.bottomOlder
this.heldBottomCenter = this.bottomPrevious
this.heldBottomRight = this.pixel
this.heldValid = true
this.heldStart = this.x == 2 && this.y == 2
this.heldLineEnd = this.x == WIDTH - 1
this.heldEnd = this.x == WIDTH - 1 && this.y == HEIGHT - 1
this.heldId = this.identity
}
this.topOlder = this.topPrevious
this.topPrevious = this.olderLine.readData
this.middleOlder = this.middlePrevious
this.middlePrevious = this.previousLine.readData
this.bottomOlder = this.bottomPrevious
this.bottomPrevious = this.pixel
this.writePhase = false
if (this.x == WIDTH - 1) {
this.x = 0
if (this.y == HEIGHT - 1) {
this.y = 0
} else {
this.y++
}
} else {
this.x++
}
} elif (this.inputValid && this.upstreamReady) {
this.pixel = this.inputData
this.identity = this.inputId
this.writePhase = true
if (this.inputStart) {
this.x = 0
this.y = 0
}
}
goto Tick
}
}
}