diff --git a/src/gateware/cxp_frame_pipeline.py b/src/gateware/cxp_frame_pipeline.py index 7f9f1f9..943da60 100644 --- a/src/gateware/cxp_frame_pipeline.py +++ b/src/gateware/cxp_frame_pipeline.py @@ -9,6 +9,7 @@ from src.gateware.cxp_pipeline import * # for sim only from types import SimpleNamespace from math import lcm +from operator import or_, add pixel_width = 16 pixel4x_layout = [ @@ -558,7 +559,7 @@ class Pixel_Parser(Module): self.pixel4x.append(Record([ ("x", res_width), ("y", res_width), - ("d", pixel_width), + ("gray", pixel_width), ("stb", 1), ("eof", 1), # end of frame ])) @@ -588,10 +589,98 @@ class Pixel_Parser(Module): 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)]), + pix.gray.eq(self.sink.data[pixel_width*i:pixel_width*(i+1)]), ) ] +class ROI(Module): + """ + ROI Engine. For each frame, accumulates pixels values within a + rectangular region of interest, and reports the total. + """ + def __init__(self, pixel_4x, count_len): + assert count_len <= 31 + assert len(pixel_4x) == 4 + + self.cfg = Record([ + ("x0", len(pixel_4x[0].x)), + ("y0", len(pixel_4x[0].y)), + ("x1", len(pixel_4x[0].x)), + ("y1", len(pixel_4x[0].y)), + ]) + + self.out = Record([ + ("update", 1), + ("count", count_len), + ]) + + # # # + # + + self.roi_4x = [] + for _ in range(4): + self.roi_4x.append(Record([ + ("x_good", 1), + ("y_good", 1), + ("gray", len(pixel_4x[0].gray)), + ("stb", 1), + ("count", count_len), + ])) + + + # x_good = [Signal() for _ in range(4)] + # y_good = [Signal() for _ in range(4)] + # gray = [Signal(len(pixel_4x[0].gray)) for _ in range(4)] + # stb = [Signal() for _ in range(4)] + # count = [Signal(count_len) for _ in range(4)] + + for pix, roi in zip(pixel_4x, self.roi_4x): + self.sync += [ + # stage 1 - generate "good" (in-ROI) signals + If(pix.x == self.cfg.x0, + roi.x_good.eq(1) + ), + # NOTE: this gate doens't work as 4 pixes are coming in + If(pix.x >= self.cfg.x1, + roi.x_good.eq(0) + ), + + # This is fine because 4x pixel are on the same line + If(pix.y == self.cfg.y0, + roi.y_good.eq(1) + ), + If(pix.y == self.cfg.y1, + roi.y_good.eq(0) + ), + If(pix.eof, + roi.x_good.eq(0), + roi.y_good.eq(0) + ), + roi.gray.eq(pix.gray), + roi.stb.eq(pix.stb), + + # stage 2 - accumulate + If((roi.stb & roi.x_good & roi.y_good), + roi.count.eq(roi.count + roi.gray) + ) + ] + + eof = Signal() + # stage 3 - update + self.sync += [ + eof.eq(reduce(or_, [pix.eof for pix in pixel_4x])), + + self.out.update.eq(0), + If(eof, + [roi.count.eq(0) for roi in self.roi_4x], + self.out.update.eq(1), + self.out.count.eq(reduce(add, [roi.count for roi in self.roi_4x])) + + ) + ] + + + class Frame_Deserializer(Module): @@ -628,6 +717,7 @@ class Frame_Deserializer(Module): self.submodules.parser = parser = Pixel_Parser(res_width) self.sync += parser.y_size.eq(self.y_size) + self.pixel4x = parser.pixel4x mux_cases = { "default": [ @@ -646,8 +736,8 @@ class Frame_Deserializer(Module): class ROI_Pipeline(Module): - # TODO: but header xsize/ysize is only 24bits, change this to 24? - def __init__(self, res_width=32): + # largest x/y pixel size supported by frame header are 24 bits + def __init__(self, res_width=24): # 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 @@ -658,6 +748,8 @@ class ROI_Pipeline(Module): self.submodules.header_decoder = header_decoder = Frame_Header_Decoder() self.submodules.deserializer = deserializer = Frame_Deserializer(res_width) + self.submodules.roi = ROI(deserializer.pixel4x, 31) + self.comb += [ deserializer.new_frame.eq(header_decoder.new_frame), deserializer.l_size.eq(header_decoder.metadata.l_size),