From d6a0b4644c870422ea811f975cf7e8a865d0ad90 Mon Sep 17 00:00:00 2001 From: morgan Date: Wed, 15 Jan 2025 11:22:45 +0800 Subject: [PATCH] frameline GW: add proto pixel parser frameline GW: update doc frameline GW: add doc --- src/gateware/cxp_frame_pipeline.py | 100 ++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 16 deletions(-) diff --git a/src/gateware/cxp_frame_pipeline.py b/src/gateware/cxp_frame_pipeline.py index dfa9f07..7f9f1f9 100644 --- a/src/gateware/cxp_frame_pipeline.py +++ b/src/gateware/cxp_frame_pipeline.py @@ -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