132 lines
4.6 KiB
Python
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
|