litex/misoclib/mem/litesata/frontend/striping.py

132 lines
4.6 KiB
Python

from misoclib.mem.litesata.common import *
from misoclib.mem.litesata.frontend.common import *
class LiteSATAStripingTX(Module):
"""SATA Striping TX
Split cmds and writes data on N different controllers.
This module provides a mirroring_mode that is used by the mirroring module to
dispatch identicals writes to the controllers. This avoid code duplication in
between striping and mirroring modules. In this special case, port's data width
is dw (same as controllers)
"""
def __init__(self, n, dw, mirroring_mode=False):
self.sink = sink = Sink(command_tx_description(dw*n if not mirroring_mode else dw))
self.sources = sources = [Source(command_tx_description(dw)) for i in range(n)]
# # #
split = Signal()
already_acked = Signal(n)
self.sync += If(split & sink.stb,
already_acked.eq(already_acked | Cat(*[s.ack for s in sources])),
If(sink.ack, already_acked.eq(0))
)
self.fsm = fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
sink.ack.eq(0),
If(sink.stb & sink.sop,
NextState("SPLIT")
).Else(
sink.ack.eq(1)
)
)
# split data and ctrl signals (except stb & ack managed in fsm)
for i, s in enumerate(sources):
self.comb += Record.connect(sink, s, leave_out=["stb", "ack", "data"])
if mirroring_mode:
self.comb += s.data.eq(sink.data)
else:
self.comb += s.data.eq(sink.data[i*dw:(i+1)*dw])
fsm.act("SPLIT",
split.eq(1),
[s.stb.eq(sink.stb & ~already_acked[i]) for i, s in enumerate(sources)],
sink.ack.eq(optree("&", [s.ack | already_acked[i] for i, s in enumerate(sources)])),
If(sink.stb & sink.eop & sink.ack,
NextState("IDLE")
)
)
class LiteSATAStripingRX(Module):
"""SATA Striping RX
Combine acknowledges and reads data from N different controllers.
This module provides a mirroring_mode that is used by the mirroring module to
dispatch identicals writes to the controllers. This avoid code duplication in
between striping and mirroring modules. In this special case, port's data width
is dw (same as controllers)
"""
def __init__(self, n, dw, mirroring_mode=False):
self.sinks = sinks = [Sink(command_rx_description(dw)) for i in range(n)]
self.source = source = Source(command_rx_description(dw*n if not mirroring_mode else dw))
# # #
sop = Signal()
self.comb += sop.eq(optree("&", [s.stb & s.sop for s in sinks]))
self.fsm = fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
If(sop,
NextState("COMBINE")
)
)
# use first sink for ctrl signals (except for stb, ack & failed)
self.comb += Record.connect(sinks[0], source, leave_out=["stb", "ack", "failed", "data"])
# combine datas
if mirroring_mode:
self.comb += source.data.eq(0) # mirroring only used for writes
else:
for i, s in enumerate(sinks):
self.comb += source.data[i*dw:(i+1)*dw].eq(s.data)
fsm.act("COMBINE",
source.failed.eq(optree("|", [s.failed for s in sinks])), # XXX verify this is enough
source.stb.eq(optree("&", [s.stb for s in sinks])),
[s.ack.eq(source.stb & source.ack) for s in sinks],
If(source.stb & source.eop & source.ack,
NextState("IDLE")
)
)
class LiteSATAStriping(Module):
"""SATA Striping
Segment data so that data is stored on N different controllers.
+----> controller0 (dw)
port (N*dw) <----+----> controllerX (dw)
+----> controllerN (dw)
Characteristics:
- port's visible capacity = N x controller's visible capacity
- port's throughput = N x (slowest) controller's throughput
Can be used to increase capacity and writes/reads throughput.
"""
def __init__(self, controllers):
# # #
n = len(controllers)
dw = flen(controllers[0].sink.data)
self.submodules.tx = LiteSATAStripingTX(n, dw)
self.submodules.rx = LiteSATAStripingRX(n, dw)
for i in range(n):
self.comb += [
Record.connect(self.tx.sources[i], controllers[i].sink),
Record.connect(controllers[i].source, self.rx.sinks[i])
]
self.sink, self.source = self.tx.sink, self.rx.source