def __init__(self, lp_pins: TristateIo, hs_pins: TristateDdrIo, initial_driving=True, can_lp=False, ddr_domain="sync"): self.lp_pins = lp_pins self.hs_pins = hs_pins self.can_lp = can_lp self.ddr_domain = ddr_domain if self.can_lp: # The control rx and control tx signals carry the raw escape mode packets. # A packet with length 1 and payload 0x0 indicates that we request a bus turnaround. # This in Not a valid MIPI Escape Entry Code so we simply repurpose that here. self.control_input = PacketizedStream(8) self.control_output = PacketizedStream(8) # the hs_input stream carries is polled to self.hs_input = PacketizedStream(8) self.is_hs = StatusSignal() self.is_driving = StatusSignal( reset=initial_driving) if can_lp else True self.bta_timeout = 1023 self.bta_timeouts = StatusSignal(16)
def elaborate(self, platform): m = Module() wavelet = m.submodules.wavelet = MultiStageWavelet2D(self.input, self.width, self.height, stages=3) packetizer = m.submodules.packetizer = ImageStream2PacketizedStream( wavelet.output) bit_stuffing_input = VariableWidthStream(self.input.payload.shape(), reset_width=len( self.input.payload)) with m.If(packetizer.output.is_hf): rle_input = PacketizedStream() m.d.comb += rle_input.connect_upstream(packetizer.output) rle = m.submodules.rle = ZeroRleEncoder(rle_input, self.possible_run_lengths) huffman = m.submodules.huffman = HuffmanEncoder(rle.output) m.d.comb += bit_stuffing_input.connect_upstream(huffman.output) with m.Else(): m.d.comb += bit_stuffing_input.connect_upstream(packetizer.output) bit_stuffing = m.submodules.bit_stuffing = BitStuffer( bit_stuffing_input, len(self.output.payload)) m.d.comb += self.output.connect_upstream(bit_stuffing.output) return m
def __init__(self, resource, num_lanes, ddr_domain, ck_domain): self.resource = resource self.num_lanes = num_lanes self.ddr_domain = ddr_domain self.ck_domain = ck_domain self.control_input = PacketizedStream(8) self.control_output = PacketizedStream(8) self.hs_input = PacketizedStream(8 * num_lanes) self.request_hs = ControlSignal()
def __init__(self, data_width=8, max_packet_size=1024): self.max_packet_size = max_packet_size self.reset = ControlSignal() self.packet_length = ControlSignal(range(max_packet_size)) self.read_ptr = StatusSignal(range(max_packet_size)) self.done = StatusSignal(reset=1) self.memory = SocMemory( width=data_width, depth=self.max_packet_size, soc_read=False, attrs=dict(syn_ramstyle="block_ram") ) self.output = PacketizedStream(data_width)
def test_simple_gearbox_384_to_64_last(self): input = PacketizedStream(32 * 12) dut = SimpleStreamGearbox(input, 64) payload_a = int("".join(reversed([f"{i:03x}" for i in range(32)])), 16) payload_b = int("".join(reversed([f"{i:03x}" for i in range(57, 57 + 32)])), 16) print(hex(payload_a)) print(hex(payload_b)) def writer(): yield from write_to_stream(input, payload=payload_a, last=1) yield from write_to_stream(input, payload=payload_b, last=0) def reader(): payload_aa = payload_a for i in range(32 * 12 // 64): self.assertEqual((yield from read_from_stream(dut.output, extract=("payload", "last"))), (payload_aa & 0xffff_ffff_ffff_ffff, 0 if i < 5 else 1)) payload_aa = payload_aa >> 64 payload_bb = payload_b for i in range(32 * 12 // 64): print(i) self.assertEqual((yield from read_from_stream(dut.output, extract=("payload", "last"))), (payload_bb & 0xffff_ffff_ffff_ffff, 0)) payload_bb = payload_bb >> 64 platform = SimPlatform() platform.add_sim_clock("sync", 100e6) platform.add_process(writer, "sync") platform.add_process(reader, "sync") platform.sim(dut)
def test_simple_gearbox_dont_loose_last_16_to_4(self): input = PacketizedStream(16) dut = SimpleStreamGearbox(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 test_long_fifo(self): platform = SimPlatform() input_stream = PacketizedStream(32) dut = LastWrapper(input_stream, lambda i: BufferedSyncStreamFIFO(i, 200), last_rle_bits=10) random.seed(0) test_packets = [ [random.randint(0, 2**32) for _ in range(12)], [random.randint(0, 2**32) for _ in range(24)], [random.randint(0, 2**32) for _ in range(1)], [random.randint(0, 2**32) for _ in range(1000)], [random.randint(0, 2**32) for _ in range(1000)], [random.randint(0, 2 ** 32) for _ in range(12)], [random.randint(0, 2 ** 32) for _ in range(1000)], ] def writer_process(): for packet in test_packets: yield from write_packet_to_stream(input_stream, packet) platform.add_process(writer_process, "sync") def reader_process(): read_packets = [] while len(read_packets) < len(test_packets): read = (yield from read_packet_from_stream(dut.output)) read_packets.append(read) print([len(p) for p in read_packets]) self.assertEqual(read_packets, test_packets) platform.add_process(reader_process, "sync") platform.add_sim_clock("sync", 100e6) platform.sim(dut)
def test_hello_world(self): platform = SimPlatform() m = Module() address_stream = PacketizedStream(8) mem = Memory(width=32, depth=128, init=[i + 2 for i in range(128)]) reader = m.submodules.reader = StreamMemoryReader(address_stream, mem) def write_process(): for i in range(128): yield from write_to_stream(address_stream, payload=i, last=(i % 8) == 0) yield Passive() def read_process(): for i in range(128): data, last = (yield from read_from_stream(reader.output, extract=("payload", "last"))) assert data == i + 2 assert last == ((i % 8) == 0) yield Passive() platform.add_sim_clock("sync", 100e6) platform.add_process(write_process, "sync") platform.sim(m, read_process)
def elaborate(self, platform): m = Module() memory = m.submodules.memory = self.memory address_stream = PacketizedStream(bits_for(self.max_packet_size)) with m.If(~self.done): m.d.comb += address_stream.valid.eq(1) m.d.comb += address_stream.last.eq(self.read_ptr == self.packet_length) m.d.comb += address_stream.payload.eq(self.read_ptr) with m.If(address_stream.ready): m.d.sync += self.read_ptr.eq(self.read_ptr + 1) m.d.sync += self.done.eq(self.read_ptr == self.packet_length) reset = Signal() m.submodules += FFSynchronizer(self.reset, reset) with m.If(Changed(m, reset)): m.d.sync += self.read_ptr.eq(0) m.d.sync += self.done.eq(0) reader = m.submodules.reader = StreamMemoryReader(address_stream, memory) buffer = m.submodules.buffer = StreamBuffer(reader.output) m.d.comb += self.output.connect_upstream(buffer.output) return m
def __init__(self, input: ImageStream, width, height): self.input = input self.width = width self.height = height max_input_word = 2**len(self.input.payload) self.possible_run_lengths = [2, 4, 8, 16, 32, 64, 128, 256] self.huffmann_frequencies = { **{k: 1 for k in range(max_input_word)}, **{ k: 10 for k in range(max_input_word, max_input_word + len(self.possible_run_lengths)) } } self.output = PacketizedStream(self.input.payload.shape())
def test_core_output_stream_contract(self): input_stream = PacketizedStream(32) device_input_stream: BasicStream def core_producer(i): nonlocal device_input_stream device_input_stream = i return LegalStreamSource(i.clone()) dut = GenericMetadataWrapper(input_stream, core_producer) verify_stream_output_contract(dut, stream_output=device_input_stream, support_modules=(LegalStreamSource(input_stream),))
def __init__(self, input: ImageStream, num_lanes: int, image_width=480, debug=False): assert len(input.payload) == 24 self.input = input self.num_lanes = num_lanes self.image_width = ControlSignal(16, reset=image_width * 3) self.debug = debug self.vbp = ControlSignal(16, reset=18) self.vfp = ControlSignal(16, reset=4) self.hbp = ControlSignal(16, reset=68 * 3) self.hfp = ControlSignal(16, reset=20 * 3) self.gearbox_not_ready = StatusSignal(32) self.output = PacketizedStream(num_lanes * 8)
class ConsolePacketSource(Elaboratable): def __init__(self, data_width=8, max_packet_size=1024): self.max_packet_size = max_packet_size self.reset = ControlSignal() self.packet_length = ControlSignal(range(max_packet_size)) self.read_ptr = StatusSignal(range(max_packet_size)) self.done = StatusSignal(reset=1) self.memory = SocMemory( width=data_width, depth=self.max_packet_size, soc_read=False, attrs=dict(syn_ramstyle="block_ram") ) self.output = PacketizedStream(data_width) def elaborate(self, platform): m = Module() memory = m.submodules.memory = self.memory address_stream = PacketizedStream(bits_for(self.max_packet_size)) with m.If(~self.done): m.d.comb += address_stream.valid.eq(1) m.d.comb += address_stream.last.eq(self.read_ptr == self.packet_length) m.d.comb += address_stream.payload.eq(self.read_ptr) with m.If(address_stream.ready): m.d.sync += self.read_ptr.eq(self.read_ptr + 1) m.d.sync += self.done.eq(self.read_ptr == self.packet_length) reset = Signal() m.submodules += FFSynchronizer(self.reset, reset) with m.If(Changed(m, reset)): m.d.sync += self.read_ptr.eq(0) m.d.sync += self.done.eq(0) reader = m.submodules.reader = StreamMemoryReader(address_stream, memory) buffer = m.submodules.buffer = StreamBuffer(reader.output) m.d.comb += self.output.connect_upstream(buffer.output) return m @driver_method def write_packet(self, packet, timeout=0): from time import sleep for i in range(int(timeout * 10)): if self.done: break sleep(0.1) assert self.done for i, word in enumerate(packet): self.memory[i] = word self.packet_length = len(packet) - 1 self.reset = not self.reset
class ImageStream2PacketizedStream(Elaboratable): """Convert an ImageStream to a packetized Stream by producing one packet per frame""" def __init__(self, input: ImageStream): self.input = input self.output = PacketizedStream(self.input.payload.shape(), name="packetized_image_stream") def elaborate(self, platform): m = Module() m.d.comb += self.output.connect_upstream( self.input, exclude=["last", "frame_last", "line_last"]) m.d.comb += self.output.last.eq(self.input.frame_last) return m
class WaveletCompressor(Elaboratable): """compresses an image stream using a wavelet compression algorithm""" def __init__(self, input: ImageStream, width, height): self.input = input self.width = width self.height = height max_input_word = 2**len(self.input.payload) self.possible_run_lengths = [2, 4, 8, 16, 32, 64, 128, 256] self.huffmann_frequencies = { **{k: 1 for k in range(max_input_word)}, **{ k: 10 for k in range(max_input_word, max_input_word + len(self.possible_run_lengths)) } } self.output = PacketizedStream(self.input.payload.shape()) def elaborate(self, platform): m = Module() wavelet = m.submodules.wavelet = MultiStageWavelet2D(self.input, self.width, self.height, stages=3) packetizer = m.submodules.packetizer = ImageStream2PacketizedStream( wavelet.output) bit_stuffing_input = VariableWidthStream(self.input.payload.shape(), reset_width=len( self.input.payload)) with m.If(packetizer.output.is_hf): rle_input = PacketizedStream() m.d.comb += rle_input.connect_upstream(packetizer.output) rle = m.submodules.rle = ZeroRleEncoder(rle_input, self.possible_run_lengths) huffman = m.submodules.huffman = HuffmanEncoder(rle.output) m.d.comb += bit_stuffing_input.connect_upstream(huffman.output) with m.Else(): m.d.comb += bit_stuffing_input.connect_upstream(packetizer.output) bit_stuffing = m.submodules.bit_stuffing = BitStuffer( bit_stuffing_input, len(self.output.payload)) m.d.comb += self.output.connect_upstream(bit_stuffing.output) return m
def __init__(self, input: PacketizedStream, core_producer, last_fifo_depth=3, last_rle_bits=10): self.last_fifo_depth = last_fifo_depth self.last_rle_bits = last_rle_bits assert hasattr(input, "last") self.input = input self.core_input = BasicStream(input.payload.shape()) self.core = core_producer(self.core_input) self.core_output = self.core.output self.error = StatusSignal(32) self.output = PacketizedStream(len(self.core_output.payload))
class DsiPhy(Elaboratable): def __init__(self, resource, num_lanes, ddr_domain, ck_domain): self.resource = resource self.num_lanes = num_lanes self.ddr_domain = ddr_domain self.ck_domain = ck_domain self.control_input = PacketizedStream(8) self.control_output = PacketizedStream(8) self.hs_input = PacketizedStream(8 * num_lanes) self.request_hs = ControlSignal() def elaborate(self, platform: Platform): resource = platform.request(*self.resource, xdr={"hs_ck": 2, **{f"hs_d{i}": 2 for i in range(self.num_lanes)}}) m = Module() lanes = [] for i in range(2): lane = DPhyDataLane( lp_pins=getattr(resource, f"lp_d{i}"), hs_pins=getattr(resource, f"hs_d{i}"), can_lp=(i == 0), ddr_domain=self.ddr_domain ) m.submodules[f"lane_d{i}"] = lane lanes.append(lane) lane0 = lanes[0] m.d.comb += lane0.control_input.connect_upstream(self.control_input) m.d.comb += self.control_output.connect_upstream(lane0.control_output) m.d.comb += self.hs_input.ready.eq(lane0.hs_input.ready) for i, lane in enumerate(lanes): m.d.comb += lane.hs_input.payload.eq(self.hs_input.payload[i * 8: (i+1) * 8]) m.d.comb += lane.hs_input.valid.eq(self.hs_input.valid) m.d.comb += lane.hs_input.last.eq(self.hs_input.last) lane_ck = m.submodules.lane_ck = DPhyClockLane(resource.lp_ck, resource.hs_ck, ck_domain=self.ck_domain) m.d.comb += lane_ck.request_hs.eq(self.request_hs) 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_hello_world(self): platform = SimPlatform() m = Module() input = PacketizedStream(8) input_data = "hello, world :)" distribution = defaultdict(lambda: 0) for c in input_data: distribution[ord(c)] += 1 huffman = m.submodules.huffman = HuffmanEncoder(input, distribution) def write_process(): for i, c in enumerate(input_data): yield from write_to_stream(input, payload=ord(c), last=(i == len(input_data) - 1)) def read_process(): read = "" while True: data, length, last = (yield from read_from_stream( huffman.output, extract=("payload", "current_width", "last"))) bitstring = "{:0255b}".format(data)[::-1][:length] read += bitstring if last: break print(read) decode_iter = bitarray(read).iterdecode( {k: bitarray(v[::-1]) for k, v in huffman.table.items()}) decoded = "" try: for c in decode_iter: decoded += chr(c) except ValueError: # Decoding may not finish with the byte boundary pass self.assertEqual(input_data, decoded) platform.add_sim_clock("sync", 100e6) platform.add_process(write_process, "sync") platform.sim(m, read_process)
def test_hello_world_bit_stuffing(self): platform = SimPlatform() m = Module() input = PacketizedStream(8) input_data = "hello, world :)" distribution = defaultdict(lambda: 0) for c in input_data: distribution[ord(c)] += 1 huffman = m.submodules.huffman = HuffmanEncoder(input, distribution) bit_stuffing = m.submodules.bit_stuffing = BitStuffer( huffman.output, 8) def write_process(): for i, c in enumerate(input_data): yield from write_to_stream(input, payload=ord(c), last=(i == len(input_data) - 1)) def read_process(): read = [] while True: payload, last = (yield from read_from_stream(bit_stuffing.output, extract=("payload", "last"))) read.append("{:08b}".format(payload)) if last: break read_bitarray = "".join(x[::-1] for x in read) print(read_bitarray) decode_iter = bitarray(read_bitarray).iterdecode( {k: bitarray(v[::-1]) for k, v in huffman.table.items()}) for c, expected in zip(decode_iter, input_data): self.assertEqual(chr(c), expected) platform.add_sim_clock("sync", 100e6) platform.add_process(write_process, "sync") platform.sim(m, read_process)
def test_PacketizedStream2ImageStream(self): platform = SimPlatform() input_stream = PacketizedStream(32) dut = PacketizedStream2ImageStream(input_stream, width=10) def write_process(): for frame in range(10): yield from write_packet_to_stream(input_stream, [0 for _ in range(100)]) platform.add_process(write_process, "sync") def read_process(): for frame in range(10): frame = yield from read_frame_from_stream(dut.output) self.assertEqual(len(frame), 10) self.assertTrue(all(len(l) == 10 for l in frame)) platform.add_process(read_process, "sync") platform.add_sim_clock("sync", 100e6) 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_output_stream_contract(self): input_stream = PacketizedStream(8) mem = Memory(width=32, depth=128, init=[i + 2 for i in range(128)]) dut = StreamMemoryReader(input_stream, mem) verify_stream_output_contract( dut, support_modules=(LegalStreamSource(input_stream), ))
def __init__(self, input: ImageStream): self.input = input self.output = PacketizedStream(self.input.payload.shape(), name="packetized_image_stream")
def __init__(self, packet_length_stream: BasicStream, data_stream: BasicStream): self.packet_length_stream = packet_length_stream self.data_stream = data_stream self.output = PacketizedStream(self.data_stream.payload.shape())
class DPhyDataLane(Elaboratable): """ A mipi D-Phy Data lane that can handle bidirectional lp data transfer and unidirectional hs transfer. The `sync` domain of this module should run at 2x the LP Hold period. Eg if the hold period is 66ns, the sync domain should run at 30 Mhz (these are reasonable values btw). This is needed to be able to sample the incoming data during bus turnaround since there is no fixed phase relation (nyquist). """ def __init__(self, lp_pins: TristateIo, hs_pins: TristateDdrIo, initial_driving=True, can_lp=False, ddr_domain="sync"): self.lp_pins = lp_pins self.hs_pins = hs_pins self.can_lp = can_lp self.ddr_domain = ddr_domain if self.can_lp: # The control rx and control tx signals carry the raw escape mode packets. # A packet with length 1 and payload 0x0 indicates that we request a bus turnaround. # This in Not a valid MIPI Escape Entry Code so we simply repurpose that here. self.control_input = PacketizedStream(8) self.control_output = PacketizedStream(8) # the hs_input stream carries is polled to self.hs_input = PacketizedStream(8) self.is_hs = StatusSignal() self.is_driving = StatusSignal( reset=initial_driving) if can_lp else True self.bta_timeout = 1023 self.bta_timeouts = StatusSignal(16) def elaborate(self, platform): m = Module() m.d.comb += self.lp_pins.oe.eq(self.is_driving) def delay_lp(cycles): return process_delay(m, cycles * 4) serializer_reset = Signal() m.d.comb += serializer_reset.eq(~(self.is_hs & self.is_driving)) serializer = m.submodules.serializer = Serializer( self.hs_pins, width=8, ddr_domain=self.ddr_domain, reset=serializer_reset) @process_block def send_hs(data): return process_write_to_stream(m, serializer.input, payload=data) bta_timeout_counter = Signal(range(self.bta_timeout)) bta_timeout_possible = Signal() with m.If(self.is_driving): lp = self.lp_pins.o[::-1] with m.FSM(name="tx_fsm") as fsm: fsm_status_reg(platform, m, fsm) with m.State("IDLE"): m.d.comb += lp.eq(STOP) if self.can_lp: with m.If(self.control_input.valid & (self.control_input.payload == 0x00) & self.control_input.last): m.d.comb += self.control_input.ready.eq(1) m.next = "TURNAROUND_LP_REQUEST" with m.Elif(self.control_input.valid): m.next = "LP_REQUEST" with m.Elif(self.hs_input.valid): m.next = "HS_REQUEST" else: with m.If(self.hs_input.valid): m.next = "HS_REQUEST" with Process(m, name="HS_REQUEST", to="HS_SEND") as p: m.d.comb += lp.eq(HS_REQUEST) p += delay_lp(1) m.d.comb += lp.eq(BRIDGE) p += delay_lp(3) m.d.sync += self.is_hs.eq(1) p += delay_lp( 4 ) # we are in HS-ZERO now and wait the constant part (150ns) p += send_hs(Repl(0, 8)) p += send_hs(Repl(0, 8)) p += send_hs(Const(0b10111000, 8)) with m.State("HS_SEND"): with send_hs(self.hs_input.payload): with m.If(self.hs_input.last): m.next = "HS_END" with m.Else(): m.next = "HS_SEND" m.d.comb += self.hs_input.ready.eq(1) with Process(m, name="HS_END", to="IDLE") as p: p += send_hs(Repl(~self.hs_input.payload[7], 8)) p += send_hs(Repl(~self.hs_input.payload[7], 8)) with m.If(NewHere(m)): m.d.comb += self.hs_input.ready.eq(1) p += m.If(serializer.is_idle) m.d.sync += self.is_hs.eq(0) p += process_delay( m, 1 ) # TODO: this is currently tied to the way we do ddr (beaks when we change clock frequencies) m.d.comb += lp.eq(STOP) p += delay_lp(2) if self.can_lp: with Process(m, name="LP_REQUEST", to="ESCAPE_0") as p: m.d.comb += lp.eq(LP_REQUEST) p += delay_lp(1) m.d.comb += lp.eq(BRIDGE) p += delay_lp(1) m.d.comb += lp.eq(ESCAPE_REQUEST) p += delay_lp(1) m.d.comb += lp.eq(BRIDGE) p += delay_lp(1) for bit in range(8): with m.State(f"ESCAPE_{bit}"): with m.If( self.control_input.valid ): # after transmitting the first byte, this can be false. the mipi spec allows us to wait here (in space state) with m.If(self.control_input.payload[bit]): m.d.comb += lp.eq(MARK_1) with m.Else(): m.d.comb += lp.eq(MARK_0) with delay_lp(1): m.next = f"ESCAPE_{bit}_SPACE" with m.State(f"ESCAPE_{bit}_SPACE"): m.d.comb += lp.eq(SPACE) if bit < 7: with delay_lp(1): m.next = f"ESCAPE_{bit + 1}" else: with m.If( self.control_input.last ): # according to the stream contract, this may not change, until we assert ready :) with delay_lp(1): m.next = "ESCAPE_FINISH" m.d.comb += self.control_input.ready.eq( 1) with m.Else(): with delay_lp(1): m.next = "ESCAPE_0" m.d.comb += self.control_input.ready.eq( 1) with Process(m, "ESCAPE_FINISH", to="IDLE") as p: m.d.comb += lp.eq(MARK_1) p += delay_lp(1) m.d.comb += lp.eq(STOP) p += delay_lp( 10 ) # TODO: reduce the delay; it is here to ease debugging :) with Process(m, name="TURNAROUND_LP_REQUEST", to="TURNAROUND_RETURN") as p: m.d.comb += lp.eq(LP_REQUEST) p += delay_lp(1) m.d.comb += lp.eq(BRIDGE) p += delay_lp(1) m.d.comb += lp.eq(TURNAROUND_REQUEST) p += delay_lp(1) m.d.comb += lp.eq(BRIDGE) p += delay_lp(4) m.d.sync += self.is_driving.eq( 0 ) # this makes us leave this FSM and enter the one below m.d.sync += bta_timeout_counter.eq(0) m.d.sync += bta_timeout_possible.eq(1) p += process_delay(m, 1) with Process(m, name="TURNAROUND_RETURN", to="IDLE") as p: m.d.comb += lp.eq(STOP) p += delay_lp(10) if self.can_lp: # we buffer the control output to be able to meet the stream contract control_output_unbuffered = PacketizedStream(8) control_output_buffer = m.submodules.control_output_buffer = StreamBuffer( control_output_unbuffered) m.d.comb += self.control_output.connect_upstream( control_output_buffer.output) with m.If(~self.is_driving): lp = Signal(2) m.submodules += FFSynchronizer(self.lp_pins.i[::-1], lp) with m.FSM(name="rx_fsm") as fsm: def maybe_next(condition, next_state): with m.If(condition): m.next = next_state def maybe_stop(): maybe_next(lp == STOP, "STOP") with m.State("STOP"): with m.If(bta_timeout_possible): with m.If(bta_timeout_counter < self.bta_timeout): m.d.sync += bta_timeout_counter.eq( bta_timeout_counter + 1) with m.Else(): m.d.sync += self.bta_timeouts.eq( self.bta_timeouts + 1) m.d.sync += self.is_driving.eq(1) m.d.sync += bta_timeout_counter.eq(0) m.d.sync += bta_timeout_possible.eq(0) maybe_next(lp == LP_REQUEST, "AFTER-LP-REQUEST") maybe_stop() with m.State("AFTER-LP-REQUEST"): m.d.sync += bta_timeout_possible.eq(0) with m.If(lp == BRIDGE): m.next = "AFTER-LP-REQUEST-BRIDGE" maybe_stop() with m.State("AFTER-LP-REQUEST-BRIDGE"): with m.If(lp == ESCAPE_REQUEST): m.next = "AFTER-ESCAPE-REQUEST" with m.Elif(lp == TURNAROUND_REQUEST): m.next = "AFTER-TURNAROUND-REQUEST" maybe_stop() with m.State("AFTER-TURNAROUND-REQUEST"): with m.If(lp == BRIDGE): with delay_lp(4): m.next = "STOP" m.d.sync += self.is_driving.eq(1) maybe_stop() with m.State("AFTER-ESCAPE-REQUEST"): with m.If(lp == BRIDGE): m.next = "ESCAPE_0" # we keep track if we have already sent the currently or last received bit over our output stream. # we send it either on the first bit of the next word or during the stop condition outboxed = Signal(reset=1) def maybe_finish_escape(): with m.If(lp == STOP): m.next = "STOP" m.d.sync += outboxed.eq(1) with m.If(~outboxed): m.d.comb += control_output_unbuffered.last.eq( 1) m.d.comb += control_output_unbuffered.valid.eq( 1) bit_value = Signal() for bit in range(8): with m.State(f"ESCAPE_{bit}"): with m.If(lp == MARK_0): m.d.sync += bit_value.eq(0) m.next = f"ESCAPE_{bit}_SPACE" with m.If(lp == MARK_1): m.d.sync += bit_value.eq(1) m.next = f"ESCAPE_{bit}_SPACE" maybe_finish_escape() with m.State(f"ESCAPE_{bit}_SPACE"): with m.If(lp == SPACE): if bit == 0: with m.If(~outboxed): m.d.comb += control_output_unbuffered.valid.eq( 1) m.d.sync += outboxed.eq(1) m.d.sync += control_output_unbuffered.payload[ bit].eq(bit_value) m.d.sync += outboxed.eq(0) m.next = f"ESCAPE_{(bit + 1) % 8}" maybe_finish_escape() return m
def elaborate(self, platform): m = Module() m.d.comb += self.lp_pins.oe.eq(self.is_driving) def delay_lp(cycles): return process_delay(m, cycles * 4) serializer_reset = Signal() m.d.comb += serializer_reset.eq(~(self.is_hs & self.is_driving)) serializer = m.submodules.serializer = Serializer( self.hs_pins, width=8, ddr_domain=self.ddr_domain, reset=serializer_reset) @process_block def send_hs(data): return process_write_to_stream(m, serializer.input, payload=data) bta_timeout_counter = Signal(range(self.bta_timeout)) bta_timeout_possible = Signal() with m.If(self.is_driving): lp = self.lp_pins.o[::-1] with m.FSM(name="tx_fsm") as fsm: fsm_status_reg(platform, m, fsm) with m.State("IDLE"): m.d.comb += lp.eq(STOP) if self.can_lp: with m.If(self.control_input.valid & (self.control_input.payload == 0x00) & self.control_input.last): m.d.comb += self.control_input.ready.eq(1) m.next = "TURNAROUND_LP_REQUEST" with m.Elif(self.control_input.valid): m.next = "LP_REQUEST" with m.Elif(self.hs_input.valid): m.next = "HS_REQUEST" else: with m.If(self.hs_input.valid): m.next = "HS_REQUEST" with Process(m, name="HS_REQUEST", to="HS_SEND") as p: m.d.comb += lp.eq(HS_REQUEST) p += delay_lp(1) m.d.comb += lp.eq(BRIDGE) p += delay_lp(3) m.d.sync += self.is_hs.eq(1) p += delay_lp( 4 ) # we are in HS-ZERO now and wait the constant part (150ns) p += send_hs(Repl(0, 8)) p += send_hs(Repl(0, 8)) p += send_hs(Const(0b10111000, 8)) with m.State("HS_SEND"): with send_hs(self.hs_input.payload): with m.If(self.hs_input.last): m.next = "HS_END" with m.Else(): m.next = "HS_SEND" m.d.comb += self.hs_input.ready.eq(1) with Process(m, name="HS_END", to="IDLE") as p: p += send_hs(Repl(~self.hs_input.payload[7], 8)) p += send_hs(Repl(~self.hs_input.payload[7], 8)) with m.If(NewHere(m)): m.d.comb += self.hs_input.ready.eq(1) p += m.If(serializer.is_idle) m.d.sync += self.is_hs.eq(0) p += process_delay( m, 1 ) # TODO: this is currently tied to the way we do ddr (beaks when we change clock frequencies) m.d.comb += lp.eq(STOP) p += delay_lp(2) if self.can_lp: with Process(m, name="LP_REQUEST", to="ESCAPE_0") as p: m.d.comb += lp.eq(LP_REQUEST) p += delay_lp(1) m.d.comb += lp.eq(BRIDGE) p += delay_lp(1) m.d.comb += lp.eq(ESCAPE_REQUEST) p += delay_lp(1) m.d.comb += lp.eq(BRIDGE) p += delay_lp(1) for bit in range(8): with m.State(f"ESCAPE_{bit}"): with m.If( self.control_input.valid ): # after transmitting the first byte, this can be false. the mipi spec allows us to wait here (in space state) with m.If(self.control_input.payload[bit]): m.d.comb += lp.eq(MARK_1) with m.Else(): m.d.comb += lp.eq(MARK_0) with delay_lp(1): m.next = f"ESCAPE_{bit}_SPACE" with m.State(f"ESCAPE_{bit}_SPACE"): m.d.comb += lp.eq(SPACE) if bit < 7: with delay_lp(1): m.next = f"ESCAPE_{bit + 1}" else: with m.If( self.control_input.last ): # according to the stream contract, this may not change, until we assert ready :) with delay_lp(1): m.next = "ESCAPE_FINISH" m.d.comb += self.control_input.ready.eq( 1) with m.Else(): with delay_lp(1): m.next = "ESCAPE_0" m.d.comb += self.control_input.ready.eq( 1) with Process(m, "ESCAPE_FINISH", to="IDLE") as p: m.d.comb += lp.eq(MARK_1) p += delay_lp(1) m.d.comb += lp.eq(STOP) p += delay_lp( 10 ) # TODO: reduce the delay; it is here to ease debugging :) with Process(m, name="TURNAROUND_LP_REQUEST", to="TURNAROUND_RETURN") as p: m.d.comb += lp.eq(LP_REQUEST) p += delay_lp(1) m.d.comb += lp.eq(BRIDGE) p += delay_lp(1) m.d.comb += lp.eq(TURNAROUND_REQUEST) p += delay_lp(1) m.d.comb += lp.eq(BRIDGE) p += delay_lp(4) m.d.sync += self.is_driving.eq( 0 ) # this makes us leave this FSM and enter the one below m.d.sync += bta_timeout_counter.eq(0) m.d.sync += bta_timeout_possible.eq(1) p += process_delay(m, 1) with Process(m, name="TURNAROUND_RETURN", to="IDLE") as p: m.d.comb += lp.eq(STOP) p += delay_lp(10) if self.can_lp: # we buffer the control output to be able to meet the stream contract control_output_unbuffered = PacketizedStream(8) control_output_buffer = m.submodules.control_output_buffer = StreamBuffer( control_output_unbuffered) m.d.comb += self.control_output.connect_upstream( control_output_buffer.output) with m.If(~self.is_driving): lp = Signal(2) m.submodules += FFSynchronizer(self.lp_pins.i[::-1], lp) with m.FSM(name="rx_fsm") as fsm: def maybe_next(condition, next_state): with m.If(condition): m.next = next_state def maybe_stop(): maybe_next(lp == STOP, "STOP") with m.State("STOP"): with m.If(bta_timeout_possible): with m.If(bta_timeout_counter < self.bta_timeout): m.d.sync += bta_timeout_counter.eq( bta_timeout_counter + 1) with m.Else(): m.d.sync += self.bta_timeouts.eq( self.bta_timeouts + 1) m.d.sync += self.is_driving.eq(1) m.d.sync += bta_timeout_counter.eq(0) m.d.sync += bta_timeout_possible.eq(0) maybe_next(lp == LP_REQUEST, "AFTER-LP-REQUEST") maybe_stop() with m.State("AFTER-LP-REQUEST"): m.d.sync += bta_timeout_possible.eq(0) with m.If(lp == BRIDGE): m.next = "AFTER-LP-REQUEST-BRIDGE" maybe_stop() with m.State("AFTER-LP-REQUEST-BRIDGE"): with m.If(lp == ESCAPE_REQUEST): m.next = "AFTER-ESCAPE-REQUEST" with m.Elif(lp == TURNAROUND_REQUEST): m.next = "AFTER-TURNAROUND-REQUEST" maybe_stop() with m.State("AFTER-TURNAROUND-REQUEST"): with m.If(lp == BRIDGE): with delay_lp(4): m.next = "STOP" m.d.sync += self.is_driving.eq(1) maybe_stop() with m.State("AFTER-ESCAPE-REQUEST"): with m.If(lp == BRIDGE): m.next = "ESCAPE_0" # we keep track if we have already sent the currently or last received bit over our output stream. # we send it either on the first bit of the next word or during the stop condition outboxed = Signal(reset=1) def maybe_finish_escape(): with m.If(lp == STOP): m.next = "STOP" m.d.sync += outboxed.eq(1) with m.If(~outboxed): m.d.comb += control_output_unbuffered.last.eq( 1) m.d.comb += control_output_unbuffered.valid.eq( 1) bit_value = Signal() for bit in range(8): with m.State(f"ESCAPE_{bit}"): with m.If(lp == MARK_0): m.d.sync += bit_value.eq(0) m.next = f"ESCAPE_{bit}_SPACE" with m.If(lp == MARK_1): m.d.sync += bit_value.eq(1) m.next = f"ESCAPE_{bit}_SPACE" maybe_finish_escape() with m.State(f"ESCAPE_{bit}_SPACE"): with m.If(lp == SPACE): if bit == 0: with m.If(~outboxed): m.d.comb += control_output_unbuffered.valid.eq( 1) m.d.sync += outboxed.eq(1) m.d.sync += control_output_unbuffered.payload[ bit].eq(bit_value) m.d.sync += outboxed.eq(0) m.next = f"ESCAPE_{(bit + 1) % 8}" maybe_finish_escape() return m
def test_output_stream_contract(self): input_stream = PacketizedStream(32) dut = GenericMetadataWrapper(input_stream, lambda i: BufferedSyncStreamFIFO(i, 10)) verify_stream_output_contract(dut, support_modules=(LegalStreamSource(input_stream),))
def test_output_stream_contract(self): input_stream = PacketizedStream(32) dut = LastWrapper(input_stream, lambda i: BufferedSyncStreamFIFO(i, 10), last_rle_bits=3) verify_stream_output_contract(dut, support_modules=(LegalStreamSource(input_stream),))
def test_last_wrapper_contract(self): dut = LastWrapperContract(LastWrapper(PacketizedStream(32), lambda i: BufferedSyncStreamFIFO(i, 10))) assert_formal(dut, mode="hybrid", depth=10)