From 1bb5a05488a7cc6fd03cd8b73ca6d693361f2b32 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Sat, 23 May 2015 14:12:20 +0200 Subject: [PATCH] litesata: add striping module for use of multiple HDDs. --- misoclib/mem/litesata/frontend/__init__.py | 1 + misoclib/mem/litesata/frontend/striping.py | 131 +++++++++++++++++++++ misoclib/mem/litesata/test/Makefile | 3 + misoclib/mem/litesata/test/striping_tb.py | 68 +++++++++++ 4 files changed, 203 insertions(+) create mode 100644 misoclib/mem/litesata/frontend/striping.py create mode 100644 misoclib/mem/litesata/test/striping_tb.py diff --git a/misoclib/mem/litesata/frontend/__init__.py b/misoclib/mem/litesata/frontend/__init__.py index 76102f2c..18a7bee3 100644 --- a/misoclib/mem/litesata/frontend/__init__.py +++ b/misoclib/mem/litesata/frontend/__init__.py @@ -1,4 +1,5 @@ from misoclib.mem.litesata.common import * from misoclib.mem.litesata.frontend.crossbar import LiteSATACrossbar from misoclib.mem.litesata.frontend.arbiter import LiteSATAArbiter +from misoclib.mem.litesata.frontend.striping import LiteSATAStriping from misoclib.mem.litesata.frontend.bist import LiteSATABIST diff --git a/misoclib/mem/litesata/frontend/striping.py b/misoclib/mem/litesata/frontend/striping.py new file mode 100644 index 00000000..60cb2055 --- /dev/null +++ b/misoclib/mem/litesata/frontend/striping.py @@ -0,0 +1,131 @@ +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 diff --git a/misoclib/mem/litesata/test/Makefile b/misoclib/mem/litesata/test/Makefile index 067511bb..0ebebb45 100644 --- a/misoclib/mem/litesata/test/Makefile +++ b/misoclib/mem/litesata/test/Makefile @@ -29,5 +29,8 @@ command_tb: bist_tb: $(CMD) bist_tb.py +striping_tb: + $(CMD) striping_tb.py + clean: rm crc scrambler *.vcd diff --git a/misoclib/mem/litesata/test/striping_tb.py b/misoclib/mem/litesata/test/striping_tb.py new file mode 100644 index 00000000..eb2fcf9d --- /dev/null +++ b/misoclib/mem/litesata/test/striping_tb.py @@ -0,0 +1,68 @@ +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) -- 2.30.2