diff --git a/src/gateware/cxp_frame_pipeline.py b/src/gateware/cxp_frame_pipeline.py index bec9e87..35efeeb 100644 --- a/src/gateware/cxp_frame_pipeline.py +++ b/src/gateware/cxp_frame_pipeline.py @@ -401,90 +401,111 @@ class Frame_Header_Decoder(Module): class Custom_Pixel_Gearbox(Module): def __init__(self, size): - assert size in [8] + assert size in [8, 16] self.x_size = Signal(3*char_width) - i_dw, o_dw = word_dw, size*4 - self.sink = stream.Endpoint([("data", i_dw)]) + sink_dw, source_dw = word_dw, size*4 + self.sink = stream.Endpoint([("data", sink_dw)]) self.source = stream.Endpoint(pixel4x_layout) # # # - io_lcm = lcm(i_dw, o_dw) - if (io_lcm//i_dw) < 2: - io_lcm = io_lcm * 2 - if (io_lcm//o_dw) < 2: - io_lcm = io_lcm * 2 + shift_reg_size = lcm(sink_dw, source_dw) + # ensure the shift register is at least twice the size of sink/source dw + if (shift_reg_size//sink_dw) < 2: + shift_reg_size = shift_reg_size * 2 + if (shift_reg_size//source_dw) < 2: + shift_reg_size = shift_reg_size * 2 + # Control interface - self.shift_register = Signal(io_lcm, reset_less=True) + reset_reg = Signal() + level = Signal(max=shift_reg_size) + we = Signal() + re = Signal() + self.w_cnt = Signal(max=shift_reg_size//sink_dw) + self.r_cnt = Signal(max=shift_reg_size//source_dw) - # Input sink - - i_inc = Signal() - i_count = Signal(max=io_lcm//i_dw) - - self.comb += [ - self.sink.ack.eq(1), # assume downstream is not blocked - i_inc.eq(self.sink.stb), - ] - self.sync += [ - If(i_inc, - If(i_count == ((io_lcm//i_dw) - 1), - i_count.eq(i_count.reset), - ).Else( - i_count.eq(i_count + 1), - ) + If(reset_reg, + level.eq(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(reset_reg, + self.w_cnt.eq(self.w_cnt.reset), + self.r_cnt.eq(self.r_cnt.reset), + ).Else( + If(we, + If(self.w_cnt == ((shift_reg_size//sink_dw) - 1), + self.w_cnt.eq(self.w_cnt.reset), + ).Else( + self.w_cnt.eq(self.w_cnt + 1), + ) + ), + If(re, + If(self.r_cnt == ((shift_reg_size//source_dw) - 1), + self.r_cnt.eq(self.r_cnt.reset), + ).Else( + self.r_cnt.eq(self.r_cnt + 1), + ) + ), + ) ] + + + self.submodules.fsm = fsm = FSM(reset_state="COPY") + + + + valid_stb = Signal() + self.comb += self.source.stb.eq((level >= source_dw) | valid_stb) - i_cases = {} - for i in range(io_lcm//i_dw): - i_cases[i] = [ - self.shift_register[i_dw*i:i_dw*(i+1)].eq(self.sink.data), + fsm.act( + "COPY", + self.sink.ack.eq(1), + # self.source.stb.eq(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"), + ), + ) + + fsm.act( + "NEWLINE", + reset_reg.eq(1), + valid_stb.eq(1), + # self.source.stb.eq(1), + NextState("COPY"), + ) + + # Data path + + self.shift_register = Signal(shift_reg_size, reset_less=True) + + sink_cases = {} + for i in range(shift_reg_size//sink_dw): + sink_cases[i] = [ + self.shift_register[sink_dw*i:sink_dw*(i+1)].eq(self.sink.data), ] - self.sync += If(self.sink.stb, Case(i_count, i_cases)) + self.sync += If(self.sink.stb, Case(self.w_cnt, sink_cases)) - # Output source - - level = Signal(max=io_lcm) - o_inc = Signal() - o_count = Signal(max=io_lcm//o_dw) - - self.comb += [ - self.source.stb.eq(level >= o_dw), - o_inc.eq(self.source.stb & self.source.ack) - ] - - self.sync += [ - If(o_inc, - If(o_count == ((io_lcm//o_dw) - 1), - o_count.eq(o_count.reset), - ).Else( - o_count.eq(o_count + 1), - ) - ), - If(i_inc & ~o_inc, level.eq(level + i_dw)), - If(~i_inc & o_inc, level.eq(level - o_dw)), - If(i_inc & o_inc, level.eq(level + i_dw - o_dw)), - ] - - o_cases = {} - for i in range(io_lcm//o_dw): - o_cases[i] = [] + source_cases = {} + for i in range(shift_reg_size//source_dw): + source_cases[i] = [] for j in range(4): - o_cases[i].append( + source_cases[i].append( self.source.data[pixel_width * j : pixel_width * (j + 1)].eq( - self.shift_register[(o_dw * i) + (size * j) : (o_dw * i) + (size * (j + 1))] + self.shift_register[(source_dw * i) + (size * j) : (source_dw * i) + (size * (j + 1))] ) ) - self.comb += Case(o_count, o_cases) - # Handle line break - - # precalcule which pixels are valid + # precalcule which last pixels are valid self.valid = Signal(4) bit_cases = { 0: self.valid.eq(0b1111), @@ -494,28 +515,16 @@ class Custom_Pixel_Gearbox(Module): } self.sync += Case(self.x_size[:2], bit_cases) - # TODO: reset the o_count & i_count after eop - line_break_r = Signal() - self.sync += [ - line_break_r.eq(self.sink.eop), - If(line_break_r, - ) - ] - - # get which last pixels are valid - # use end of line to reset o_count, i_count, level & stb the last pixel self.comb += [ - If(line_break_r, + Case(self.r_cnt, source_cases), + If(valid_stb, self.source.valid.eq(self.valid), ).Else( self.source.valid.eq(0b1111), - ) + ), ] - - - class Frame_Deserializer(Module): def __init__(self, width, pixel_size): self.new_frame = Signal() @@ -528,7 +537,7 @@ class Frame_Deserializer(Module): self.sync += eol_inserter.l_size.eq(self.l_size), - self.submodules.gearbox = gearbox = Custom_Pixel_Gearbox(8) + self.submodules.gearbox = gearbox = Custom_Pixel_Gearbox(16) self.sync += gearbox.x_size.eq(self.x_size), self.comb += eol_inserter.source.connect(gearbox.sink)