From 03b6c4577caa3e66c1556f410b72c260c56733cd Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 27 Jun 2024 18:23:23 +0100 Subject: [PATCH] WIP: Insert HyperRAMPacketFIFO into output pipeline. --- .../python/src/gateware/analyzer/analyzer.py | 2 +- cynthion/python/src/gateware/analyzer/fifo.py | 108 ++++++++++++++++++ cynthion/python/src/gateware/analyzer/top.py | 10 +- 3 files changed, 116 insertions(+), 4 deletions(-) diff --git a/cynthion/python/src/gateware/analyzer/analyzer.py b/cynthion/python/src/gateware/analyzer/analyzer.py index 24733b62..085bccfe 100644 --- a/cynthion/python/src/gateware/analyzer/analyzer.py +++ b/cynthion/python/src/gateware/analyzer/analyzer.py @@ -64,7 +64,7 @@ class USBAnalyzer(Elaboratable): # Support a maximum payload size of 1024B, plus a 1-byte PID and a 2-byte CRC16. MAX_PACKET_SIZE_BYTES = 1024 + 1 + 2 - def __init__(self, *, utmi_interface, mem_depth=32768): + def __init__(self, *, utmi_interface, mem_depth=4096): """ Parameters: utmi_interface -- A record or elaboratable that presents a UTMI interface. diff --git a/cynthion/python/src/gateware/analyzer/fifo.py b/cynthion/python/src/gateware/analyzer/fifo.py index 5ac828e8..287f715b 100644 --- a/cynthion/python/src/gateware/analyzer/fifo.py +++ b/cynthion/python/src/gateware/analyzer/fifo.py @@ -8,6 +8,7 @@ from amaranth.lib.fifo import SyncFIFO from luna.gateware.stream import StreamInterface +from luna.gateware.interface.psram import HyperRAMInterface, HyperRAMPHY class StreamFIFO(Elaboratable): @@ -34,6 +35,113 @@ def elaborate(self, platform): return m +class HyperRAMPacketFIFO(Elaboratable): + def __init__(self): + self.input = StreamInterface(payload_width=16) + self.output = StreamInterface(payload_width=16) + + def elaborate(self, platform): + m = Module() + + # HyperRAM submodules + ram_bus = platform.request('ram') + psram_phy = HyperRAMPHY(bus=ram_bus) + psram = HyperRAMInterface(phy=psram_phy.phy) + m.submodules += [psram_phy, psram] + + # HyperRAM status + depth = 2 ** 22 + write_address = Signal(range(depth)) + read_address = Signal(range(depth)) + word_count = Signal(range(depth + 1)) + empty = Signal() + full = Signal() + m.d.comb += [ + empty .eq(word_count == 0), + full .eq(word_count == depth), + ] + + # Update word count and pointers using the write and read strobes. + m.d.sync += word_count.eq(word_count - psram.read_ready + psram.write_ready) + with m.If(psram.read_ready): + m.d.sync += read_address.eq(read_address + 1) + with m.If(psram.write_ready): + m.d.sync += write_address.eq(write_address + 1) + + # This tiny output buffer prevents data loss during consumer stalls + m.submodules.out_fifo = out_fifo = SyncFIFO(width=16, depth=2) + + # Hook up our PSRAM. + m.d.comb += [ + ram_bus.reset.o .eq(0), + psram.single_page .eq(0), + psram.register_space .eq(0), + psram.write_data .eq(self.input.payload), + self.input.ready .eq(psram.write_ready), + + # Wire PSRAM -> output FIFO -> output stream + out_fifo.w_data .eq(psram.read_data), + out_fifo.w_en .eq(psram.read_ready), + self.output.payload .eq(out_fifo.r_data), + self.output.valid .eq(out_fifo.r_rdy), + out_fifo.r_en .eq(self.output.ready), + ] + + # + # HyperRAM Packet FIFO state machine + # + with m.FSM(domain="sync"): + + # IDLE: Begin a write / read burst operation when ready. + with m.State("IDLE"): + with m.If(self.input.valid & ~full): + m.d.comb += [ + psram.address .eq(write_address), + psram.perform_write .eq(1), + psram.start_transfer .eq(1), + psram.final_word .eq(self.input.last), + ] + with m.If(psram.final_word): + m.next = "FINISH" + with m.Else(): + m.next = "WRITE" + + with m.Elif((out_fifo.level == 0) & ~empty): + m.d.comb += [ + psram.address .eq(read_address), + psram.perform_write .eq(0), + psram.start_transfer .eq(1), + psram.final_word .eq(word_count == 1), + ] + with m.If(psram.final_word): + m.next = "FINISH" + with m.Else(): + m.next = "READ" + + # WRITE: End the operation when there's no space or incoming data. + with m.State("WRITE"): + m.d.comb += [ + psram.final_word.eq((word_count == (depth-1)) | self.input.last), + ] + with m.If(psram.final_word): + m.next = "FINISH" + + # READ: End the operation when PSRAM is empty or the consumer stalls the output stream. + with m.State("READ"): + m.d.comb += [ + psram.final_word.eq((word_count == 1 + psram.read_ready) | ~self.output.ready), + ] + with m.If(psram.final_word): + m.next = "FINISH" + + # FINISH: Wait for the PSRAM to recover before a new transaction + with m.State("FINISH"): + with m.If(psram.idle): + m.next = "IDLE" + + return m + + class Stream16to8(Elaboratable): def __init__(self, msb_first=True): self.msb_first = msb_first diff --git a/cynthion/python/src/gateware/analyzer/top.py b/cynthion/python/src/gateware/analyzer/top.py index 3764510f..a38982eb 100644 --- a/cynthion/python/src/gateware/analyzer/top.py +++ b/cynthion/python/src/gateware/analyzer/top.py @@ -42,7 +42,7 @@ from usb_protocol.types.descriptors.microsoft10 import RegistryTypes from .analyzer import USBAnalyzer -from .fifo import Stream16to8, StreamFIFO +from .fifo import Stream16to8, StreamFIFO, HyperRAMPacketFIFO import cynthion @@ -329,8 +329,11 @@ def elaborate(self, platform): # Create a USB analyzer. m.submodules.analyzer = analyzer = USBAnalyzer(utmi_interface=utmi) - # Add an additional FIFO for 'sync' to 'usb' crossing. + # Follow this with a HyperRAM FIFO for additional buffering. reset_on_start = ResetInserter(analyzer.discarding) + m.submodules.psram_fifo = psram_fifo = reset_on_start(HyperRAMPacketFIFO()) + + # Add an additional FIFO for 'sync' to 'usb' crossing. m.submodules.out_fifo = out_fifo = StreamFIFO(reset_on_start( AsyncFIFO(width=16, depth=4096, r_domain="usb", w_domain="sync"))) @@ -348,7 +351,8 @@ def elaborate(self, platform): stream_ep.discard .eq(analyzer.discarding), # USB stream pipeline. - out_fifo.input .stream_eq(analyzer.stream), + psram_fifo.input .stream_eq(analyzer.stream), + out_fifo.input .stream_eq(psram_fifo.output), s16to8.input .stream_eq(out_fifo.output), stream_ep.stream .stream_eq(s16to8.output),