Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
whitequark committed Jul 30, 2024
1 parent caed4d5 commit 9941607
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from amaranth.lib.wiring import In, Out, connect, flipped

from ....support.logging import *
from ....gateware.iostream import IOStream
from ....gateware.iostream import IOStreamer
from ....gateware.qspi import QSPIMode, QSPIController
from ... import *

Expand Down
59 changes: 57 additions & 2 deletions software/glasgow/gateware/iostream.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from glasgow.gateware.ports import PortGroup


__all__ = ["IOStream"]
__all__ = ["IOStreamer"]


def _iter_ioshape(ioshape, *args):
Expand All @@ -25,7 +25,7 @@ def _map_ioshape(ioshape, fn):
assert False


class IOStream(wiring.Component):
class IOStreamer(wiring.Component):
"""I/O buffer to stream adapter.
This adapter instantiates I/O buffers for a port (FF or DDR) and connects them to a pair of
Expand Down Expand Up @@ -151,3 +151,58 @@ def delay(value, name):
m.d.comb += self.o_stream.ready.eq(self.i_stream.ready & (skid_at == 0))

return m


class IOClocker(wiring.Component):
def __init__(self, clock, ioshape, *, o_ratio=1, meta_layout=0, divisor_width=16):
assert isinstance(ioshape, dict)
assert isinstance(clock, str)
assert o_ratio in (1, 2)

self._clock = clock
self._i_ioshape = ioshape
self._o_ioshape = {clock: 1, **ioshape}
self._o_ratio = o_ratio

super().__init__({
"i_stream": In(IOStreamer.o_stream_signature(self._i_ioshape, ratio=1,
meta_layout=meta_layout)),
"o_stream": Out(IOStreamer.o_stream_signature(self._o_ioshape, ratio=o_ratio,
meta_layout=meta_layout)),

"divisor": In(divisor_width),
})

def elaborate(self, platform):
m = Module()

for i_parts, o_parts in _iter_ioshape(self._i_ioshape, self.i_stream.p.port, self.o_stream.p.port):
m.d.comb += o_parts.o .eq(i_parts.o.replicate(self._o_ratio))
m.d.comb += o_parts.oe.eq(i_parts.oe)
m.d.comb += self.o_stream.p.meta.eq(self.i_stream.p.meta)

clock = Signal()
if self._o_ratio == 1:
m.d.comb += self.o_stream.p.port[self._clock].o.eq(clock)
if self._o_ratio == 2:
m.d.comb += self.o_stream.p.port[self._clock].o.eq(Cat(~clock, clock))
m.d.comb += self.o_stream.p.port[self._clock].oe.eq(1)
m.d.comb += self.o_stream.p.i_en.eq(self.i_stream.p.i_en & clock)

timer = Signal.like(self.divisor)
phase = Signal()
with m.If((timer == 0) | (timer == 1)):
m.d.comb += self.o_stream.valid.eq(self.i_stream.valid)
m.d.comb += self.i_stream.ready.eq(self.o_stream.ready & clock)
with m.If(self.i_stream.valid & self.o_stream.ready):
with m.If((self._o_ratio == 2) & (self.divisor == 0)):
m.d.sync += phase.eq(0)
m.d.comb += clock.eq(1)
with m.Else():
m.d.sync += phase.eq(~phase)
m.d.comb += clock.eq(phase)
m.d.sync += timer.eq(self.divisor)
with m.Else():
m.d.sync += timer.eq(timer - 1)

return m
125 changes: 34 additions & 91 deletions software/glasgow/gateware/qspi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from amaranth.lib.wiring import In, Out, connect, flipped

from .ports import PortGroup
from .iostream import IOStream
from .iostream import IOStreamer, IOClocker


__all__ = ["QSPIMode", "QSPIEnframer", "QSPIDeframer", "QSPIController"]
Expand All @@ -30,7 +30,7 @@ def __init__(self, *, chip_count=1):
"mode": QSPIMode,
"data": 8,
}))),
"frames": Out(IOStream.o_stream_signature({
"frames": Out(IOStreamer.o_stream_signature({
"io0": 1,
"io1": 1,
"io2": 1,
Expand Down Expand Up @@ -93,11 +93,13 @@ def elaborate(self, platform):
class QSPIDeframer(wiring.Component): # meow :3
def __init__(self):
super().__init__({
"frames": In(IOStream.i_stream_signature({
"frames": In(IOStreamer.i_stream_signature({
"sck": 1, # FIXME: should not be needed
"io0": 1,
"io1": 1,
"io2": 1,
"io3": 1,
"cs": 1, # FIXME: should not be needed
}, meta_layout=QSPIMode)),
"octets": Out(stream.Signature(data.StructLayout({
"data": 8,
Expand Down Expand Up @@ -138,46 +140,23 @@ def elaborate(self, platform):
return m


# FIXME: needs new name and location
class Downscaler(wiring.Component):
def __init__(self, payload_shape, *, divisor_width=16):
super().__init__({
"divisor": In(divisor_width),

# FIXME: i_stream/o_stream or fast/slow? [io]_stream has opposite meaning of IOStream
"fast": In(stream.Signature(payload_shape)),
"slow": Out(stream.Signature(payload_shape)),
})

def elaborate(self, platform):
m = Module()

timer = Signal.like(self.divisor)
with m.If((timer == 0) | (timer == 1)):
m.d.comb += self.slow.valid.eq(self.fast.valid)
m.d.comb += self.fast.ready.eq(self.slow.ready)
with m.If(self.fast.valid & self.slow.ready):
m.d.sync += timer.eq(self.divisor)
with m.Else():
m.d.sync += timer.eq(timer - 1)

m.d.comb += self.slow.payload.eq(self.fast.payload)

return m


class QSPIController(wiring.Component):
def __init__(self, ports, *, chip_count=1, use_ddr_buffers=False):
assert len(ports.sck) == 1 and ports.sck.direction in (io.Direction.Output, io.Direction.Bidir)
assert len(ports.io) == 4 and ports.io.direction == io.Direction.Bidir
assert len(ports.cs) >= 1 and ports.cs.direction in (io.Direction.Output, io.Direction.Bidir)

self._ports = ports
self._ports = PortGroup(
sck=ports.sck,
io0=ports.io[0],
io1=ports.io[1],
io2=ports.io[2],
io3=ports.io[3],
cs=~ports.cs,
)
self._ddr = use_ddr_buffers

super().__init__({
"divisor": In(16),

"o_octets": In(stream.Signature(data.StructLayout({
"chip": range(1 + chip_count),
"mode": QSPIMode,
Expand All @@ -186,81 +165,45 @@ def __init__(self, ports, *, chip_count=1, use_ddr_buffers=False):
"i_octets": Out(stream.Signature(data.StructLayout({
"data": 8
}))),

"divisor": In(16),
})

def elaborate(self, platform):
ratio = (2 if self._ddr else 1)

m = Module()

m.submodules.iostream = iostream = IOStream({
m.submodules.enframer = enframer = QSPIEnframer()
connect(m, controller=flipped(self.o_octets), enframer=enframer.octets)

# FIXME: make clock toggle only when CS# is active
m.submodules.io_clocker = io_clocker = IOClocker("sck", {
"io0": 1,
"io1": 1,
"io2": 1,
"io3": 1,
"cs": len(self._ports.cs),
}, o_ratio=ratio, meta_layout=QSPIMode)
connect(m, enframer=enframer.frames, io_clocker=io_clocker.i_stream)
m.d.comb += io_clocker.divisor.eq(self.divisor)

m.submodules.io_streamer = io_streamer = IOStreamer({
"sck": 1,
"io0": 1,
"io1": 1,
"io2": 1,
"io3": 1,
"cs": len(self._ports.cs),
}, PortGroup(
sck=self._ports.sck,
io0=self._ports.io[0],
io1=self._ports.io[1],
io2=self._ports.io[2],
io3=self._ports.io[3],
cs=~self._ports.cs,
), init={
}, self._ports, init={
"sck": {"o": 1, "oe": 1}, # Motorola "Mode 3" with clock idling high
"cs": {"o": 0, "oe": 1}, # deselected
}, ratio=ratio, meta_layout=QSPIMode)

m.submodules.downscaler = downscaler = Downscaler(iostream.o_stream.payload.shape(),
divisor_width=len(self.divisor))
connect(m, downscaler=downscaler.slow, iostream=iostream.o_stream)
m.d.comb += downscaler.divisor.eq(self.divisor)

m.submodules.enframer = enframer = QSPIEnframer()
connect(m, controller=flipped(self.o_octets), enframer=enframer.octets)
connect(m, io_clocker=io_clocker.o_stream, io_streamer=io_streamer.o_stream)

m.submodules.deframer = deframer = QSPIDeframer()
connect(m, controller=flipped(self.i_octets), deframer=deframer.octets)

phase = Signal()
with m.If(self._ddr & (self.divisor == 0)): # special case: transfer each cycle
m.d.sync += phase.eq(1)
with m.Elif(iostream.o_stream.valid): # half-transfer or less each cycle
m.d.sync += phase.eq(~phase)
with m.If(enframer.frames.p.port.cs.o.any()):
if self._ddr:
m.d.comb += downscaler.fast.p.port.sck.o.eq(Cat(~phase, phase))
else:
m.d.comb += downscaler.fast.p.port.sck.o.eq(phase)
with m.Else():
m.d.comb += downscaler.fast.p.port.sck.o.eq(C(1).replicate(ratio))
m.d.comb += [
downscaler.fast.p.port.sck.oe.eq(enframer.frames.p.port.cs.o.any()),
downscaler.fast.p.port.io0.o.eq(enframer.frames.p.port.io0.o.replicate(ratio)),
downscaler.fast.p.port.io1.o.eq(enframer.frames.p.port.io1.o.replicate(ratio)),
downscaler.fast.p.port.io2.o.eq(enframer.frames.p.port.io2.o.replicate(ratio)),
downscaler.fast.p.port.io3.o.eq(enframer.frames.p.port.io3.o.replicate(ratio)),
downscaler.fast.p.port.cs.o.eq(enframer.frames.p.port.cs.o.replicate(ratio)),
downscaler.fast.p.port.io0.oe.eq(enframer.frames.p.port.io0.oe),
downscaler.fast.p.port.io1.oe.eq(enframer.frames.p.port.io1.oe),
downscaler.fast.p.port.io2.oe.eq(enframer.frames.p.port.io2.oe),
downscaler.fast.p.port.io3.oe.eq(enframer.frames.p.port.io3.oe),
downscaler.fast.p.port.cs.oe.eq(enframer.frames.p.port.cs.oe),
downscaler.fast.p.i_en.eq(enframer.frames.p.i_en & phase),
downscaler.fast.p.meta.eq(enframer.frames.p.meta),
downscaler.fast.valid.eq(enframer.frames.valid),
enframer.frames.ready.eq(downscaler.fast.ready & phase),
]
connect(m, io_streamer=io_streamer.i_stream, deframer=deframer.frames)

m.d.comb += [
deframer.frames.p.port.io0.i.eq(iostream.i_stream.p.port.io0.i[0]),
deframer.frames.p.port.io1.i.eq(iostream.i_stream.p.port.io1.i[0]),
deframer.frames.p.port.io2.i.eq(iostream.i_stream.p.port.io2.i[0]),
deframer.frames.p.port.io3.i.eq(iostream.i_stream.p.port.io3.i[0]),
deframer.frames.p.meta.eq(iostream.i_stream.p.meta),
deframer.frames.valid.eq(iostream.i_stream.valid),
iostream.i_stream.ready.eq(deframer.frames.ready),
]
connect(m, deframer=deframer.octets, controller=flipped(self.i_octets))

return m
4 changes: 2 additions & 2 deletions software/tests/gateware/test_iostream.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
class IOStreamTestCase(unittest.TestCase):
def test_basic(self):
port = io.SimulationPort("io", 1)
dut = IOStream(1, port, meta_layout=4)
dut = IOStreamer(1, port, meta_layout=4)

async def testbench(ctx):
await ctx.tick()
Expand Down Expand Up @@ -58,7 +58,7 @@ async def testbench(ctx):

def test_skid(self):
port = io.SimulationPort("io", 4)
dut = IOStream(4, port, meta_layout=4)
dut = IOStreamer(4, port, meta_layout=4)

async def testbench(ctx):
await ctx.tick()
Expand Down

0 comments on commit 9941607

Please sign in to comment.