litesata: add mirroring
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 25 May 2015 12:03:14 +0000 (14:03 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 25 May 2015 12:03:14 +0000 (14:03 +0200)
misoclib/mem/litesata/example_designs/targets/mirroring.py [new file with mode: 0644]
misoclib/mem/litesata/example_designs/test/test_mirroring.py [new file with mode: 0644]
misoclib/mem/litesata/frontend/mirroring.py [new file with mode: 0644]
misoclib/mem/litesata/test/Makefile
misoclib/mem/litesata/test/mirroring_tb.py [new file with mode: 0644]

diff --git a/misoclib/mem/litesata/example_designs/targets/mirroring.py b/misoclib/mem/litesata/example_designs/targets/mirroring.py
new file mode 100644 (file)
index 0000000..1a9659b
--- /dev/null
@@ -0,0 +1,98 @@
+from misoclib.mem.litesata.common import *
+from migen.genlib.cdc import *
+from migen.genlib.resetsync import AsyncResetSynchronizer
+from migen.bank.description import *
+
+from misoclib.soc import SoC
+
+from misoclib.tools.litescope.common import *
+from misoclib.tools.litescope.frontend.la import LiteScopeLA
+from misoclib.tools.litescope.core.port import LiteScopeTerm
+
+from misoclib.com.uart.bridge import UARTWishboneBridge
+
+from misoclib.mem.litesata.common import *
+from misoclib.mem.litesata.phy import LiteSATAPHY
+from misoclib.mem.litesata.core import LiteSATACore
+from misoclib.mem.litesata.frontend.crossbar import LiteSATACrossbar
+from misoclib.mem.litesata.frontend.mirroring import LiteSATAMirroring
+from misoclib.mem.litesata.frontend.bist import LiteSATABIST
+
+from misoclib.mem.litesata.example_designs.targets.bist import CRG, StatusLeds
+
+
+class MirroringSoC(SoC, AutoCSR):
+    default_platform = "kc705"
+    csr_map = {
+        "sata_bist0": 16,
+        "sata_bist1": 17,
+        "sata_bist2": 18,
+        "sata_bist3": 19,
+    }
+    csr_map.update(SoC.csr_map)
+    def __init__(self, platform):
+        clk_freq = 166*1000000
+        SoC.__init__(self, platform, clk_freq,
+            cpu_type="none",
+            with_csr=True, csr_data_width=32,
+            with_uart=False,
+            with_identifier=True,
+            with_timer=False
+        )
+        self.add_cpu_or_bridge(UARTWishboneBridge(platform.request("serial"), clk_freq, baudrate=115200))
+        self.add_wb_master(self.cpu_or_bridge.wishbone)
+        self.submodules.crg = CRG(platform)
+
+        # SATA PHYs
+        sata_phy0 = LiteSATAPHY(platform.device, platform.request("sata_clocks"), platform.request("sata", 0), "sata_gen2", clk_freq)
+        sata_phy1 = LiteSATAPHY(platform.device, sata_phy0.crg.refclk, platform.request("sata", 1), "sata_gen2", clk_freq)
+        sata_phy2 = LiteSATAPHY(platform.device, sata_phy0.crg.refclk, platform.request("sata", 2), "sata_gen2", clk_freq)
+        sata_phy3 = LiteSATAPHY(platform.device, sata_phy0.crg.refclk, platform.request("sata", 3), "sata_gen2", clk_freq)
+        sata_phys = [sata_phy0, sata_phy1, sata_phy2, sata_phy3]
+        for i, sata_phy in enumerate(sata_phys):
+            sata_phy = RenameClockDomains(sata_phy, {"sata_rx": "sata_rx{}".format(str(i)),
+                                                     "sata_tx": "sata_tx{}".format(str(i))})
+            setattr(self.submodules, "sata_phy{}".format(str(i)), sata_phy)
+
+        # SATA Cores
+        self.submodules.sata_core0 = LiteSATACore(self.sata_phy0)
+        self.submodules.sata_core1 = LiteSATACore(self.sata_phy1)
+        self.submodules.sata_core2 = LiteSATACore(self.sata_phy2)
+        self.submodules.sata_core3 = LiteSATACore(self.sata_phy3)
+        sata_cores = [self.sata_core0, self.sata_core1, self.sata_core2, self.sata_core3]
+
+        # SATA Frontend
+        self.submodules.sata_mirroring = LiteSATAMirroring(sata_cores)
+        self.submodules.sata_crossbar0 = LiteSATACrossbar(self.sata_mirroring.ports[0])
+        self.submodules.sata_crossbar1 = LiteSATACrossbar(self.sata_mirroring.ports[1])
+        self.submodules.sata_crossbar2 = LiteSATACrossbar(self.sata_mirroring.ports[2])
+        self.submodules.sata_crossbar3 = LiteSATACrossbar(self.sata_mirroring.ports[3])
+
+        # SATA Application
+        self.submodules.sata_bist0 = LiteSATABIST(self.sata_crossbar0, with_csr=True)
+        self.submodules.sata_bist1 = LiteSATABIST(self.sata_crossbar1, with_csr=True)
+        self.submodules.sata_bist2 = LiteSATABIST(self.sata_crossbar2, with_csr=True)
+        self.submodules.sata_bist3 = LiteSATABIST(self.sata_crossbar3, with_csr=True)
+
+        # Status Leds
+        self.submodules.status_leds = StatusLeds(platform, sata_phys)
+
+
+        platform.add_platform_command("""
+create_clock -name sys_clk -period 6 [get_nets sys_clk]
+""")
+
+        for i in range(len(sata_phys)):
+            platform.add_platform_command("""
+create_clock -name {sata_rx_clk} -period 6.66 [get_nets {sata_rx_clk}]
+create_clock -name {sata_tx_clk} -period 6.66 [get_nets {sata_tx_clk}]
+
+set_false_path -from [get_clocks sys_clk] -to [get_clocks {sata_rx_clk}]
+set_false_path -from [get_clocks sys_clk] -to [get_clocks {sata_tx_clk}]
+set_false_path -from [get_clocks {sata_rx_clk}] -to [get_clocks sys_clk]
+set_false_path -from [get_clocks {sata_tx_clk}] -to [get_clocks sys_clk]
+""".format(sata_rx_clk="sata_rx{}_clk".format(str(i)),
+           sata_tx_clk="sata_tx{}_clk".format(str(i))))
+
+
+default_subtarget = MirroringSoC
\ No newline at end of file
diff --git a/misoclib/mem/litesata/example_designs/test/test_mirroring.py b/misoclib/mem/litesata/example_designs/test/test_mirroring.py
new file mode 100644 (file)
index 0000000..44449ec
--- /dev/null
@@ -0,0 +1,43 @@
+import sys
+from bist import *
+from misoclib.tools.litescope.software.driver.la import LiteScopeLADriver
+
+
+def main(wb):
+    identifys = []
+    generators = []
+    checkers = []
+    for i in range(4):
+        identifys.append(LiteSATABISTIdentifyDriver(wb.regs, "sata_bist{:d}".format(i)))
+        generators.append(LiteSATABISTGeneratorDriver(wb.regs, "sata_bist{:d}".format(i)))
+        checkers.append(LiteSATABISTCheckerDriver(wb.regs, "sata_bist{:d}".format(i)))
+
+    wb.open()
+    regs = wb.regs
+    # # #
+
+    print("Identify HDDs:")
+    print("-"*80)
+    for i, identify in enumerate(identifys):
+        identify.run()
+        print("HDD{:d}:".format(i))
+        print("-"*40)
+        identify.hdd_info()
+        print("")
+
+    print("Test Mirroring:")
+    print("-"*80)
+    errors = 0
+    for sector in range(8):
+        # Write with one generator, verify with all checkers.
+        # Use generator number as sector offset to ensure we
+        # are not reading data written by another generator.
+        for offset, generator in enumerate(generators):
+            generator.run(sector + offset, 1024, 1, 1)
+            for checker in checkers:
+                a, e, s = checker.run(sector + offset, 1024, 1, 1)
+                errors += e
+    print("errors {:d}".format(errors))
+
+    # # #
+    wb.close()
diff --git a/misoclib/mem/litesata/frontend/mirroring.py b/misoclib/mem/litesata/frontend/mirroring.py
new file mode 100644 (file)
index 0000000..67d60a8
--- /dev/null
@@ -0,0 +1,182 @@
+from migen.actorlib.packet import Arbiter, Dispatcher, Status
+from migen.flow.plumbing import Multiplexer
+
+from misoclib.mem.litesata.common import *
+from misoclib.mem.litesata.frontend.common import *
+from misoclib.mem.litesata.frontend.striping import LiteSATAStripingTX, LiteSATAStripingRX
+
+
+class LiteSATAMirroringCtrl(Module):
+    def __init__(self, n):
+        self.new_cmds = Signal(n)
+        self.ack_cmds = Signal(n)
+
+        self.reading = Signal()
+        self.writing = Signal()
+
+        self.wants_write = Signal()
+        self.write_sel = Signal(max=n)
+
+        # # #
+
+        pending_cmds = Signal(n)
+        self.sync += pending_cmds.eq(self.new_cmds | (pending_cmds & ~self.ack_cmds))
+        can_commute = Signal()
+        self.comb += can_commute.eq((pending_cmds | self.new_cmds)  == 0)
+
+        self.fsm = fsm = FSM(reset_state="READ")
+        self.submodules += fsm
+        fsm.act("READ",
+            self.reading.eq(1),
+            If(self.wants_write & can_commute,
+                NextState("WRITE")
+            )
+        )
+        fsm.act("WRITE",
+            self.writing.eq(1),
+            If(~self.wants_write & can_commute,
+                NextState("READ")
+            )
+        )
+
+
+class LiteSATAMirroringTX(Module):
+    def __init__(self, n, dw, ctrl):
+        self.sinks = sinks = [Sink(command_tx_description(dw)) for i in range(n)]
+        self.sources = sources = [Source(command_tx_description(dw)) for i in range(n)]
+
+        # # #
+
+        wants_write = Signal()
+
+        reading = Signal()
+        writing = Signal()
+
+        reads = [Sink(command_tx_description(dw)) for i in range(dw)]
+        writes = [Sink(command_tx_description(dw)) for i in range(dw)]
+        for sink, read, write in zip(sinks, reads, writes):
+            read_stall = Signal()
+            read_status = Status(read)
+            self.submodules += read_status
+            self.comb += [
+                Record.connect(sink, read, leave_out=["stb", "ack"]),
+                Record.connect(sink, write, leave_out=["stb", "ack"]),
+                read.stb.eq(sink.stb & (sink.read | sink.identify) & ~read_stall),
+                write.stb.eq(sink.stb & sink.write),
+                If(sink.read | sink.identify,
+                    sink.ack.eq((read.ack & ~read_stall))
+                ).Else(
+                    sink.ack.eq(write.ack)
+                )
+            ]
+            self.sync += \
+                If(~ctrl.wants_write,
+                    read_stall.eq(0)
+                ).Elif(~read_status.ongoing,
+                    read_stall.eq(1)
+                )
+
+        write_striper = LiteSATAStripingTX(n, dw, mirroring_mode=True)
+        write_arbiter = Arbiter(writes, write_striper.sink)
+        self.submodules += write_striper, write_arbiter
+
+        for i in range(n):
+            source_status = Status(sources[i])
+            self.submodules += source_status
+            self.comb += [
+                If(ctrl.reading,
+                    Record.connect(reads[i], sources[i]) # independent reads
+                ).Elif(ctrl.writing,
+                    Record.connect(write_striper.sources[i], sources[i]) # identical writes
+                ),
+                ctrl.new_cmds[i].eq(source_status.eop)
+            ]
+        write_striper_sink_status = Status(write_striper.sink)
+        self.submodules += write_striper_sink_status
+        self.comb += [
+            ctrl.wants_write.eq(write_striper_sink_status.ongoing),
+            ctrl.write_sel.eq(write_arbiter.rr.grant)
+        ]
+
+
+class LiteSATAMirroringRX(Module):
+    def __init__(self, n, dw, ctrl):
+        self.sinks = sinks = [Sink(command_rx_description(dw)) for i in range(n)]
+        self.sources = sources = [Source(command_rx_description(dw)) for i in range(n)]
+
+        # # #
+
+        muxs = [Multiplexer(command_rx_description(dw), 2) for i in range(n)]
+        self.submodules += muxs
+
+        writes = [mux.sink0 for mux in muxs]
+        reads = [mux.sink1 for mux in muxs]
+
+        for mux, source in zip(muxs, sources):
+            self.comb += [
+                mux.sel.eq(ctrl.reading),
+                Record.connect(mux.source, source)
+            ]
+
+        write_striper = LiteSATAStripingRX(n, dw, mirroring_mode=True)
+        write_dispatcher = Dispatcher(write_striper.source, writes)
+        self.comb += write_dispatcher.sel.eq(ctrl.write_sel)
+        self.submodules += write_striper, write_dispatcher
+
+        for i in range(n):
+            sink_status = Status(sinks[i])
+            self.submodules += sink_status
+            self.comb += [
+                Record.connect(sinks[i], reads[i], leave_out=["stb", "ack"]),
+                Record.connect(sinks[i], write_striper.sinks[i], leave_out=["stb", "ack"]),
+                reads[i].stb.eq(sinks[i].stb & ctrl.reading),
+                write_striper.sinks[i].stb.eq(sinks[i].stb & ctrl.writing),
+                sinks[i].ack.eq(reads[i].ack | write_striper.sinks[i].ack),
+                ctrl.ack_cmds[i].eq(sink_status.eop & sinks[i].last)
+            ]
+
+
+class LiteSATAMirroring(Module):
+    """SATA Mirroring
+
+    The mirroring module handles N controllers and provides N ports.
+    Each port has its dedicated controller for reads:
+        port0 <----> controller0
+        portX <----> controllerX
+        portN <----> controllerN
+
+    Writes are mirrored on each controller:
+                   (port0 write)           |            (portN write)
+        port0 ----------+----> controller0 | port0 (stalled) +-----> controller0
+        portX (stalled) +----> controllerX | portX (stalled) +-----> controllerX
+        portN (stalled) +----> controllerN | portN ----------+-----> controllerN
+
+    Writes have priority on reads. When a write is presented on one of the port, the
+    module waits for all ongoing reads to finish and commute to write mode. Once all writes are
+    serviced it returns to read mode.
+
+    Characteristics:
+        - port's visible capacity = controller's visible capacity
+        - total writes throughput = (slowest) controller's throughput
+        - total reads throughput = N x controller's throughput
+
+    Can be used for data redundancy and/or to increase total reads speed.
+    """
+    def __init__(self, controllers):
+        n = len(controllers)
+        dw = flen(controllers[0].sink.data)
+        self.ports = [LiteSATAUserPort(dw) for i in range(n)]
+
+        # # #
+
+        self.submodules.ctrl = LiteSATAMirroringCtrl(n)
+        self.submodules.tx = LiteSATAMirroringTX(n, dw, self.ctrl)
+        self.submodules.rx = LiteSATAMirroringRX(n, dw, self.ctrl)
+        for i in range(n):
+            self.comb += [
+                Record.connect(self.ports[i].sink, self.tx.sinks[i]),
+                Record.connect(self.tx.sources[i], controllers[i].sink),
+
+                Record.connect(controllers[i].source, self.rx.sinks[i]),
+                Record.connect(self.rx.sources[i], self.ports[i].source)
+            ]
index 0ebebb456963386a9de7c9208ad47148091aa9e4..48228e975d92496aac781984fd7667bf708e4835 100644 (file)
@@ -32,5 +32,8 @@ bist_tb:
 striping_tb:
        $(CMD) striping_tb.py
 
+mirroring_tb:
+       $(CMD) mirroring_tb.py
+
 clean:
        rm crc scrambler *.vcd
diff --git a/misoclib/mem/litesata/test/mirroring_tb.py b/misoclib/mem/litesata/test/mirroring_tb.py
new file mode 100644 (file)
index 0000000..4787df5
--- /dev/null
@@ -0,0 +1,76 @@
+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.mirroring import LiteSATAMirroring
+
+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.mirroring = LiteSATAMirroring([self.core0, self.core1])
+
+        self.submodules.crossbar0 = LiteSATACrossbar(self.mirroring.ports[0])
+        self.submodules.generator0 = LiteSATABISTGenerator(self.crossbar0.get_port())
+        self.submodules.checker0 = LiteSATABISTChecker(self.crossbar0.get_port())
+
+        self.submodules.crossbar1 = LiteSATACrossbar(self.mirroring.ports[1])
+        self.submodules.generator1 = LiteSATABISTGenerator(self.crossbar1.get_port())
+        self.submodules.checker1 = LiteSATABISTChecker(self.crossbar1.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
+        checker0 = selfp.checker0
+        checker1 = selfp.checker1
+        while True:
+            for generator in [selfp.generator0, selfp.generator1]:
+                # write data (alternate generators)
+                generator.sector = sector
+                generator.count = count
+                generator.start = 1
+                yield
+                generator.start = 0
+                yield
+                while generator.done == 0:
+                    yield
+
+                # verify data on the 2 hdds in //
+                checker0.sector = sector
+                checker0.count = count
+                checker0.start = 1
+                checker1.sector = sector
+                checker1.count = count
+                checker1.start = 1
+                yield
+                checker0.start = 0
+                checker1.start = 0
+                yield
+                while (checker0.done == 0) or (checker1.done == 0):
+                    yield
+                print("errors {}".format(checker0.errors + checker1.errors))
+
+                # prepare next iteration
+                sector += 1
+
+if __name__ == "__main__":
+    run_simulation(TB(), ncycles=4096, vcd_name="my.vcd", keep_files=True)