diff --git a/src/gateware/cxp_frame_pipeline.py b/src/gateware/cxp_frame_pipeline.py index 55916e3..d8729cb 100644 --- a/src/gateware/cxp_frame_pipeline.py +++ b/src/gateware/cxp_frame_pipeline.py @@ -401,7 +401,7 @@ class Frame_Header_Decoder(Module): class Custom_Pixel_Gearbox(Module): def __init__(self, size): - assert size in [8, 16] + assert size in [8, 10, 16] self.x_size = Signal(3*char_width) @@ -421,19 +421,19 @@ class Custom_Pixel_Gearbox(Module): # Control interface reset_reg = Signal() - level = Signal(max=shift_reg_size) we = Signal() re = Signal() + self.level = Signal(max=shift_reg_size) self.w_cnt = Signal(max=shift_reg_size//sink_dw) self.r_cnt = Signal(max=shift_reg_size//source_dw) self.sync += [ If(reset_reg, - level.eq(level.reset), + self.level.eq(self.level.reset), ).Else( - If(we & ~re, level.eq(level + sink_dw)), - If(~we & re, level.eq(level - source_dw)), - If(we & re, level.eq(level + sink_dw - source_dw)), + If(we & ~re, self.level.eq(self.level + sink_dw)), + If(~we & re, self.level.eq(self.level - source_dw)), + If(we & re, self.level.eq(self.level + sink_dw - source_dw)), ), If(reset_reg, @@ -456,28 +456,81 @@ class Custom_Pixel_Gearbox(Module): ), ) ] + + # FIXME: handle linebreak properly for all bits + # 1) the COPY may or may not stb data out + # 2) the NEWLINE may stb too much + # FIXME: the fsm need to be intellgent enough to know whether it need to stb twice or once at eop + # need a better generic solution + # FIXME: handle last line when the word also are aligned to 4x pixel + + # FACT: + # - self.level is only useful for normal ops (i.e. non-linebreak) + # - it should not be used for last pixels + # - extra_stb should be reserved for non aligned data only + # - after last word, fsm may need to stb 0-2 times + # - which are determined by x_size (varies) & pix size (constant) + # - self.valid should be used instead of extra stb when aligned + + extra_stb = Signal() + match size: + case 8 | 16: + # no need for extra stb as they are aligned to word boundary no matter what + pass + case 10: + stb_cases = { + 5: extra_stb.eq(1), + 6: extra_stb.eq(1), + 9: extra_stb.eq(1), + } + # mod 16 + self.sync += Case(self.x_size[:4], stb_cases) + # case 12: + # stb_cases = { + # 5: extra_stb.eq(1), + # } + # # mod 8 + # self.sync += Case(self.x_size[:3], stb_cases) - self.submodules.fsm = fsm = FSM(reset_state="COPY") + # NOTE: + # access to levels -> know when 4x pixels are + # access to x_size -> know which pixel at last stb are valid + # access to eop -> know when end of line ares + + self.submodules.fsm = fsm = FSM(reset_state="COPY_ALIGNED") fsm.act( - "COPY", + "COPY_ALIGNED", self.sink.ack.eq(1), - self.source.stb.eq(level >= source_dw), + self.source.stb.eq(self.level >= source_dw), we.eq(self.sink.stb), re.eq((self.source.stb & self.source.ack)), If(self.sink.stb & self.sink.eop, - NextState("NEWLINE"), + If(extra_stb, + NextState("EXTRA_STB"), + ).Else( + NextState("NEWLINE"), + ) ), ) + fsm.act( + "EXTRA_STB", + self.source.stb.eq(self.level >= source_dw), + re.eq((self.source.stb & self.source.ack)), + NextState("NEWLINE"), + ) + + # determine to stb 1 or 2 times + # only the last stb will use valid_stb valid_stb = Signal() fsm.act( "NEWLINE", + self.source.stb.eq(1), reset_reg.eq(1), valid_stb.eq(1), - self.source.stb.eq(1), - NextState("COPY"), + NextState("COPY_ALIGNED"), ) # Data path @@ -533,7 +586,10 @@ class Frame_Deserializer(Module): self.sync += eol_inserter.l_size.eq(self.l_size), - self.submodules.gearbox = gearbox = Custom_Pixel_Gearbox(16) + # TODO: fix the edge case where 5 x_size is not working + # the issue arises when the 4th pixel is sent with eop (e.g. 10bits & x_size = 5,6) + # eating the rest of the bits + self.submodules.gearbox = gearbox = Custom_Pixel_Gearbox(12) self.sync += gearbox.x_size.eq(self.x_size), self.comb += eol_inserter.source.connect(gearbox.sink)