Skip to content

Commit

Permalink
WIP: Insert HyperRAMPacketFIFO into output pipeline.
Browse files Browse the repository at this point in the history
  • Loading branch information
martinling committed Jun 28, 2024
1 parent 965f5d1 commit 03b6c45
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 4 deletions.
2 changes: 1 addition & 1 deletion cynthion/python/src/gateware/analyzer/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
108 changes: 108 additions & 0 deletions cynthion/python/src/gateware/analyzer/fifo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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
Expand Down
10 changes: 7 additions & 3 deletions cynthion/python/src/gateware/analyzer/top.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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")))

Expand All @@ -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),

Expand Down

0 comments on commit 03b6c45

Please sign in to comment.