--- /dev/null
+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:
+ - master's visible capacity = N x controller's visible capacity
+ - master'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
--- /dev/null
+from misoclib.mem.litesata.common import *
+from misoclib.mem.litesata.core import LiteSATACore
+from misoclib.mem.litesata.frontend.common import *
+from misoclib.mem.litesata.frontend.crossbar import LiteSATACrossbar
+from misoclib.mem.litesata.frontend.bist import LiteSATABISTGenerator, LiteSATABISTChecker
+from misoclib.mem.litesata.frontend.striping import LiteSATAStriping
+
+from misoclib.mem.litesata.test.common import *
+from misoclib.mem.litesata.test.model.hdd import *
+
+
+class TB(Module):
+ def __init__(self):
+ self.submodules.hdd0 = HDD(n=0,
+ link_debug=False, link_random_level=0,
+ transport_debug=False, transport_loopback=False,
+ hdd_debug=True)
+ self.submodules.core0 = LiteSATACore(self.hdd0.phy)
+
+ self.submodules.hdd1 = HDD(n=1,
+ link_debug=False, link_random_level=0,
+ transport_debug=False, transport_loopback=False,
+ hdd_debug=True)
+ self.submodules.core1 = LiteSATACore(self.hdd1.phy)
+
+ self.submodules.striping = LiteSATAStriping([self.core0, self.core1])
+ self.submodules.crossbar = LiteSATACrossbar(self.striping)
+
+ self.submodules.generator = LiteSATABISTGenerator(self.crossbar.get_port())
+ self.submodules.checker = LiteSATABISTChecker(self.crossbar.get_port())
+
+ def gen_simulation(self, selfp):
+ hdd0 = self.hdd0
+ hdd0.malloc(0, 64)
+ hdd1 = self.hdd1
+ hdd1.malloc(0, 64)
+ sector = 0
+ count = 1
+ generator = selfp.generator
+ checker = selfp.checker
+ while True:
+ # write data
+ generator.sector = sector
+ generator.count = count
+ generator.start = 1
+ yield
+ generator.start = 0
+ yield
+ while generator.done == 0:
+ yield
+
+ # verify data
+ checker.sector = sector
+ checker.count = count
+ checker.start = 1
+ yield
+ checker.start = 0
+ yield
+ while checker.done == 0:
+ yield
+ print("errors {}".format(checker.errors))
+
+ # prepare next iteration
+ sector += 1
+ count = max((count + 1)%8, 1)
+
+if __name__ == "__main__":
+ run_simulation(TB(), ncycles=8192*2, vcd_name="my.vcd", keep_files=True)