--- /dev/null
+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
--- /dev/null
+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()
--- /dev/null
+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)
+ ]
striping_tb:
$(CMD) striping_tb.py
+mirroring_tb:
+ $(CMD) mirroring_tb.py
+
clean:
rm crc scrambler *.vcd
--- /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.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)