def test_gearbox_12_to_48_to_64(self): m = Module() platform = SimPlatform() input = BasicStream(12) tee = m.submodules.tee = StreamTee(input) gear_12_to_48 = m.submodules.gear_12_to_48 = StreamGearbox(tee.get_output(), 48) gear_48_to_64 = m.submodules.gear_48_to_64 = StreamGearbox(gear_12_to_48.output, 64) gear_12_to_64 = m.submodules.gear_12_to_64 = StreamGearbox(tee.get_output(), 64) def writer(): yield Passive() random.seed(0) while True: yield from write_to_stream(input, payload=random.randrange(0, 2**12)) platform.add_process(writer, "sync") def reader(): for _ in range(100): a = yield from read_from_stream(gear_12_to_64.output) b = yield from read_from_stream(gear_48_to_64.output) print(f"{a:064b}") self.assertEqual(a, b) platform.add_process(reader, "sync") platform.add_sim_clock("sync", 100e6) platform.sim(m)
def test_dont_loose_last_16_to_4(self): input = PacketizedStream(16) dut = StreamGearbox(input, 4) def writer(): last_count_gold = 0 for i in range(50): last = (i % 5 == 0) last_count_gold += last yield from write_to_stream(input, payload=0, last=(i % 5 == 0)) if i % 3 == 0: yield from do_nothing() self.assertEqual(last_count_gold, 10) def reader(): last_count = 0 for i in range(200): last_count += (yield from read_from_stream(dut.output, extract="last")) if i % 10 == 0: yield from do_nothing() self.assertEqual(last_count, 10) platform = SimPlatform() platform.add_sim_clock("sync", 100e6) platform.add_process(writer, "sync") platform.add_process(reader, "sync") platform.sim(dut)
def elaborate(self, platform): m = Module() word_domain = f'{self.domain_prefix}_word' ddr_domain = f'{self.domain_prefix}_ddr' clock_phy = m.submodules.clock_phy = MipiClockRxPhy( self.clock_pin, ddr_domain) lane_phys = [ CsiWordAligner(pin, ddr_domain, self.in_packet) for pin in self.data_pins ] for i, phy in enumerate(lane_phys): m.submodules[f'lane_{i + 1}_phy'] = phy # training of multiple lanes: # if some (but not all) lanes assert maybe_first_packet_byte we disable the training logic for the ones that asserted maybe_first_packet_byte # and let the others continue their Training. This however can lead to endless loops (when some lane is trained on not the sync pattern but some # other piece of data). To reduce the likeliness that this happens we only re-enable the training logic if the training did not work after a timeout # (measured in assertions of maybe_first_packet_byte) timeout = 27 timeout_ctr = Signal(range(timeout)) def reset_successive_training(): m.d.sync += timeout_ctr.eq(0) for l in lane_phys: m.d.sync += l.enable_train_logic.eq(1) mfpb = [l.maybe_first_packet_byte for l in lane_phys] with m.If(~self.in_packet & nAll(mfpb)): # we completed training successfully reset_successive_training() with m.Elif(~self.in_packet & nAny(mfpb) & ~nAll( l.maybe_first_packet_byte | l.enable_train_logic for l in lane_phys)): # we are certainly not on the right path reset_successive_training() with m.Elif(~self.in_packet & nAny(mfpb) & (timeout_ctr < timeout)): m.d.sync += timeout_ctr.eq(timeout_ctr + 1) for l in lane_phys: with m.If(l.maybe_first_packet_byte & nAny( l.maybe_first_packet_byte & ~l.enable_train_logic for l in lane_phys)): m.d.sync += l.enable_train_logic.eq(0) with m.Elif(~self.in_packet & nAny(mfpb)): reset_successive_training() gearbox_input = PacketizedFirstStream(8 * len(lane_phys)) m.d.comb += gearbox_input.first.eq(nAll(mfpb)) for i, l in enumerate(lane_phys): m.d.comb += gearbox_input.payload[i * 8:(i + 1) * 8].eq(l.output) gearbox = m.submodules.gearbox = StreamGearbox(gearbox_input, target_width=32) m.d.comb += self.output.connect_upstream(gearbox.output) return m
def test_gearbox_8_to_4_last(self): input = PacketizedStream(8) dut = StreamGearbox(input, 4) def writer(): yield from write_to_stream(input, payload=0b00_10_00_01, last=1) yield from write_to_stream(input, payload=0b10_00_01_00, last=0) def reader(): self.assertEqual((yield from read_from_stream(dut.output, extract=("payload", "last"))), (0b0001, 0)) self.assertEqual((yield from read_from_stream(dut.output, extract=("payload", "last"))), (0b0010, 1)) self.assertEqual((yield from read_from_stream(dut.output, extract=("payload", "last"))), (0b0100, 0)) self.assertEqual((yield from read_from_stream(dut.output, extract=("payload", "last"))), (0b1000, 0)) platform = SimPlatform() platform.add_sim_clock("sync", 100e6) platform.add_process(writer, "sync") platform.add_process(reader, "sync") platform.sim(dut)
def test_gearbox_3_to_7(self): input = BasicStream(3) dut = StreamGearbox(input, 7) def writer(): yield from write_to_stream(input, payload=0b001) yield from write_to_stream(input, payload=0b010) yield from write_to_stream(input, payload=0b100) yield from write_to_stream(input, payload=0b011) yield from write_to_stream(input, payload=0b110) yield from write_to_stream(input, payload=0b111) yield from write_to_stream(input, payload=0b000) def reader(): self.assertEqual((yield from read_from_stream(dut.output)), 0b0_010_001) self.assertEqual((yield from read_from_stream(dut.output)), 0b10_011_10) self.assertEqual((yield from read_from_stream(dut.output)), 0b000_111_1) platform = SimPlatform() platform.add_sim_clock("sync", 100e6) platform.add_process(writer, "sync") platform.add_process(reader, "sync") platform.sim(dut)
def test_gearbox(input_width, output_width): input = PacketizedStream(input_width) m = Module() fifo_in = m.submodules.fifo_in = BufferedSyncStreamFIFO(input, 100) gearbox = m.submodules.gearbox = StreamGearbox(fifo_in.output, output_width) fifo_out = m.submodules.fifo_out = BufferedSyncStreamFIFO(gearbox.output, 100) input_data, output_data = gold_gen(input_width, output_width) def writer(): for v in input_data: yield from write_to_stream(input, payload=v) def reader(): for i, v in enumerate(output_data): read = (yield from read_from_stream(fifo_out.output)) self.assertEqual(read, v) platform = SimPlatform() platform.add_sim_clock("sync", 100e6) platform.add_process(writer, "sync") platform.add_process(reader, "sync") platform.sim(m)
def test_dont_loose_data(self): input = BasicStream(16) dut = StreamGearbox(input, 8) def writer(): for i in range(0, 100, 2): yield from write_to_stream(input, payload=(((i + 1) << 8) | i)) if i % 7 == 0: yield from do_nothing() def reader(): for i in range(100): got = (yield from read_from_stream(dut.output, extract="payload")) print(got) self.assertEqual(got, i) if i % 3 == 0: yield from do_nothing() platform = SimPlatform() platform.add_sim_clock("sync", 100e6) platform.add_process(writer, "sync") platform.add_process(reader, "sync") platform.sim(dut)
def test_output_stream_contract(self): input = BasicStream(7) verify_stream_output_contract(StreamGearbox(input, 3))
def elaborate(self, platform): m = Module() gearbox = m.submodules.gearbox = StreamGearbox( self.input, target_width=len(self.output.payload)) def repack_to_lanes(packet): values = [ packet[i * 8:(i + 1) * 8] for i in range(len(packet) // 8) ] if self.num_lanes == 1: pass elif self.num_lanes == 2: values = [ Cat(values[0:2]), Cat(values[2:4]), ] elif self.num_lanes == 4: values = Cat(values) else: raise AssertionError("Invalid number of lanes!") return [(v, i == len(values) - 1) for i, v in enumerate(values)] def short_packet_words(type, payload=Const(0, 16)): data_id = DataIdentifier(data_type=type, virtual_channel_identifier=0) packet_prelim = PacketHeader(data_id=data_id, word_count=payload, ecc=0) packet = PacketHeader(data_id=data_id, word_count=payload, ecc=packet_prelim.calculate_ecc()) return repack_to_lanes(packet.as_value()) def send_short_packet(p, type, payload=Const(0, 16), lp_after=False): for value, last in short_packet_words(type, payload): p += process_write_to_stream(m, self.output, payload=value, last=last & lp_after) def end_of_transmission(p): send_short_packet( p, DsiShortPacketDataType.END_OF_TRANSMISSION_PACKET, lp_after=True) blanking_counter = Signal(16) is_ready = Signal() def blanking(p, length, omit_footer=False, type=DsiLongPacketDataType.BLANKING_PACKET_NO_DATA): length_without_overhead = length - 6 m.d.sync += blanking_counter.eq(0) if not isinstance(length_without_overhead, Value): length_without_overhead = Const(length_without_overhead, 16) send_short_packet(p, type, length_without_overhead[0:16]) with process_write_to_stream(m, self.output, payload=0x0): m.d.sync += blanking_counter.eq(blanking_counter + 1) m.d.comb += is_ready.eq(1) p += m.If( blanking_counter + is_ready >= length_without_overhead // 2) # the packet overhead is 6 bytes (4 header and 2 footer) if not omit_footer: p += process_write_to_stream(m, self.output, payload=0x0) # checksum frame_last = Signal() v_porch_counter = Signal(16) def v_porch(name, to, length, skip_first_hsync=False): if not skip_first_hsync: first_name = name second_process_name = f"{name}_OVERHEAD" else: first_name = f"{name}_HSYNC" second_process_name = name with Process(m, first_name, to=second_process_name) as p: send_short_packet(p, DsiShortPacketDataType.H_SYNC_START) with Process(m, second_process_name, to=None) as p: blanking(p, self.hfp + self.image_width + self.hbp, type=DsiLongPacketDataType.NULL_PACKET_NO_DATA, omit_footer=True) with process_write_to_stream(m, self.output, payload=0x0): with m.If(v_porch_counter < length): m.d.sync += v_porch_counter.eq(v_porch_counter + 1) m.next = first_name with m.Else(): m.d.sync += v_porch_counter.eq(0) m.next = to trig = Signal() if self.debug and False: probe(m, self.output.valid) probe(m, self.output.ready) probe(m, self.output.last) probe(m, self.output.payload) probe(m, gearbox.output.ready) probe(m, gearbox.output.valid) probe(m, gearbox.output.frame_last) probe(m, gearbox.output.line_last) probe(m, gearbox.output.payload) # trigger(m, trig) with m.FSM() as fsm: fsm_status_reg(platform, m, fsm) if self.debug: ... #fsm_probe(m, fsm) with Process(m, "VSYNC_START", to="VBP") as p: send_short_packet(p, DsiShortPacketDataType.V_SYNC_START) v_porch("VBP", "LINE_START", self.vbp, skip_first_hsync=True) with Process(m, "LINE_START", to="LINE_DATA") as p: end_of_transmission(p) p += m.If(gearbox.output.valid) m.d.comb += trig.eq(1) send_short_packet(p, DsiShortPacketDataType.H_SYNC_START) blanking(p, self.hbp) send_short_packet( p, DsiLongPacketDataType.PACKED_PIXEL_STREAM_24_BIT_RGB_8_8_8, self.image_width) with m.State("LINE_DATA"): with m.If(gearbox.output.line_last & gearbox.output.valid & gearbox.output.ready): m.next = "LINE_END" m.d.sync += frame_last.eq(gearbox.output.frame_last) with m.If(~gearbox.output.valid): m.d.sync += self.gearbox_not_ready.eq( self.gearbox_not_ready + 1) m.d.comb += self.output.connect_upstream(gearbox.output, allow_partial=True) with Process(m, "LINE_END", to=None) as p: p += process_write_to_stream( m, self.output, payload=0x0) # TODO: handle the non 2 lane case blanking( p, self.hfp, omit_footer=True ) # we omit the footer to be able to do dispatch the next state with zero cycle delay with process_write_to_stream(m, self.output, payload=0x0): with m.If(frame_last): m.next = "VFP" with m.Else(): m.next = "LINE_START" v_porch("VFP", "FRAME_END", self.vfp) with Process(m, "FRAME_END", to="VSYNC_START") as p: end_of_transmission(p) return m