frameline GW: add proto pixel parser

frameline GW: update doc

frameline GW: add doc
This commit is contained in:
morgan 2025-01-15 11:22:45 +08:00
parent a02d6fce85
commit d6a0b4644c

View File

@ -4,8 +4,8 @@ from misoc.interconnect.csr import *
from misoc.interconnect import stream
from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine
from cxp_pipeline import *
# from src.gateware.cxp_pipeline import * # for sim only
# from cxp_pipeline import *
from src.gateware.cxp_pipeline import * # for sim only
from types import SimpleNamespace
from math import lcm
@ -118,8 +118,8 @@ class CXPCRC32(Module):
self.error.eq(reg != self.check),
]
# For verifying crc in stream data packet
class CXPCRC32_Checker(Module):
"""Verify crc in stream data packet"""
def __init__(self):
self.error_cnt = Signal(16)
@ -285,7 +285,6 @@ class Frame_Header_Decoder(Module):
# # #
# TODO: decode Image header, line break
self.sink = stream.Endpoint(word_layout_dchar)
self.source = stream.Endpoint(word_layout_dchar)
@ -363,7 +362,9 @@ class Frame_Header_Decoder(Module):
idx += size
class Pixel_Gearbox(Module):
"""Convert 32 bits word into 4x pixel"""
def __init__(self, size):
assert size <= pixel_width
assert size in [8, 10, 12, 14, 16]
self.x_size = Signal(3*char_width)
@ -483,12 +484,12 @@ class Pixel_Gearbox(Module):
NextState("MOVE_REMAINING_PIX"),
)
stb_valid = Signal()
stb_remaining_pix = Signal()
fsm.act(
"MOVE_REMAINING_PIX",
reset_reg.eq(1),
self.source.stb.eq(1),
stb_valid.eq(1),
stb_remaining_pix.eq(1),
NextState("SHIFTING"),
)
@ -525,26 +526,87 @@ class Pixel_Gearbox(Module):
self.comb += [
Case(r_cnt, source_cases),
If(stb_valid,
If(stb_remaining_pix,
self.source.valid.eq(valid),
self.source.eop.eq(1),
).Else(
self.source.valid.eq(0b1111),
),
]
class Pixel_Parser(Module):
"""Parses the 4x pixels and track pixel coordinates."""
def __init__(self, res_width):
self.y_size = Signal(3*char_width)
self.sink = stream.Endpoint(pixel4x_layout)
# # #
# NOTE: no need for last_x/last_y csr which is use to indicate how big is the frame
# layout = Record([
# ("x", res_width),
# ("y", res_width),
# ("d", pixel_width),
# ("stb", 1),
# ("eof", 1), # end of frame
# ])
# self.pixel4x = [layout for _ in range(4)]
# DEBUG: for sim only, to show all record in sim
self.pixel4x = []
for _ in range(4):
self.pixel4x.append(Record([
("x", res_width),
("y", res_width),
("d", pixel_width),
("stb", 1),
("eof", 1), # end of frame
]))
x_4x = [Signal(len(self.pixel4x[0].x), reset=i) for i in range(4)]
y_r = Signal(len(self.pixel4x[0].y))
self.sync += self.sink.ack.eq(1),
for i, (x_r, pix) in enumerate(zip(x_4x, self.pixel4x)):
self.sync += [
pix.stb.eq(0),
pix.eof.eq(0),
If(self.sink.stb,
If(self.sink.eop,
# new line
x_r.eq(x_r.reset),
If(y_r == self.y_size - 1,
pix.eof.eq(y_r == self.y_size - 1),
y_r.eq(0),
).Else(
y_r.eq(y_r + 1),
)
).Else(
x_r.eq(x_r + 4),
),
pix.stb.eq(self.sink.valid[i]),
pix.x.eq(x_r),
pix.y.eq(y_r),
pix.d.eq(self.sink.data[pixel_width*i:pixel_width*(i+1)]),
)
]
class Frame_Deserializer(Module):
def __init__(self, width, pixel_size):
def __init__(self, res_width):
# TODO: use new_frame or remove it
self.new_frame = Signal()
self.l_size = Signal(3*char_width)
self.x_size = Signal(3*char_width)
self.y_size = Signal(3*char_width)
self.pixel_format = Signal(2*char_width)
self.source = stream.Endpoint(pixel4x_layout)
# # #
self.submodules.eol_inserter = eol_inserter = End_Of_Line_Inserter()
self.sync += eol_inserter.l_size.eq(self.l_size),
self.sync += eol_inserter.l_size.eq(self.l_size)
self.sink = eol_inserter.sink
@ -564,16 +626,19 @@ class Frame_Deserializer(Module):
"mono16": 0x0105,
}
self.submodules.parser = parser = Pixel_Parser(res_width)
self.sync += parser.y_size.eq(self.y_size)
mux_cases = {
"default": [
# discard unknown pixel format data
# discard unknown pixel format
eol_inserter.source.ack.eq(1),
],
}
for fmt in pix_fmt:
mux_cases[pix_fmt[fmt]] = [
eol_inserter.source.connect(gearboxes[fmt].sink),
gearboxes[fmt].source.connect(self.source),
gearboxes[fmt].source.connect(parser.sink),
]
self.comb += Case(self.pixel_format, mux_cases)
@ -581,7 +646,8 @@ class Frame_Deserializer(Module):
class ROI_Pipeline(Module):
def __init__(self, res_width=32, pixel_size=16):
# TODO: but header xsize/ysize is only 24bits, change this to 24?
def __init__(self, res_width=32):
# NOTE: csr need to stay outside since this module need to be cdr in the CXP_FRAME_Pipeline module
# NOTE: TapGeo other than 1X-1Y are not supported
@ -590,12 +656,13 @@ class ROI_Pipeline(Module):
self.submodules.buffer = buffer = Buffer(word_layout_dchar) # to improve timing from broadcaster
self.submodules.crc_checker = crc_checker = CXPCRC32_Checker()
self.submodules.header_decoder = header_decoder = Frame_Header_Decoder()
self.submodules.deserializer = deserializer = Frame_Deserializer(res_width, pixel_size)
self.submodules.deserializer = deserializer = Frame_Deserializer(res_width)
self.comb += [
deserializer.new_frame.eq(header_decoder.new_frame),
deserializer.l_size.eq(header_decoder.metadata.l_size),
deserializer.x_size.eq(header_decoder.metadata.x_size),
deserializer.y_size.eq(header_decoder.metadata.y_size),
deserializer.pixel_format.eq(header_decoder.metadata.pixel_format),
]
@ -605,5 +672,6 @@ class ROI_Pipeline(Module):
self.sink = self.pipeline[0].sink
# DEBUG
self.source = self.pipeline[-1].source
self.comb += self.source.ack.eq(1) # simulated a proper consumer, idk why but without this it will destory timing
# self.pix = self.pipeline[-1].pix
# self.source = self.pipeline[-1].source
# self.comb += self.source.ack.eq(1) # simulated a proper consumer, idk why but without this it will destory timing