From 0d1a7b93153bc1b474e5a903a09b99798f9385f3 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 25 May 2015 14:03:14 +0200 Subject: [PATCH] litesata: add mirroring --- .../example_designs/targets/mirroring.py | 98 ++++++++++ .../example_designs/test/test_mirroring.py | 43 +++++ misoclib/mem/litesata/frontend/mirroring.py | 182 ++++++++++++++++++ misoclib/mem/litesata/test/Makefile | 3 + misoclib/mem/litesata/test/mirroring_tb.py | 76 ++++++++ 5 files changed, 402 insertions(+) create mode 100644 misoclib/mem/litesata/example_designs/targets/mirroring.py create mode 100644 misoclib/mem/litesata/example_designs/test/test_mirroring.py create mode 100644 misoclib/mem/litesata/frontend/mirroring.py create mode 100644 misoclib/mem/litesata/test/mirroring_tb.py diff --git a/misoclib/mem/litesata/example_designs/targets/mirroring.py b/misoclib/mem/litesata/example_designs/targets/mirroring.py new file mode 100644 index 00000000..1a9659b2 --- /dev/null +++ b/misoclib/mem/litesata/example_designs/targets/mirroring.py @@ -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 index 00000000..44449ec2 --- /dev/null +++ b/misoclib/mem/litesata/example_designs/test/test_mirroring.py @@ -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 index 00000000..67d60a89 --- /dev/null +++ b/misoclib/mem/litesata/frontend/mirroring.py @@ -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) + ] diff --git a/misoclib/mem/litesata/test/Makefile b/misoclib/mem/litesata/test/Makefile index 0ebebb45..48228e97 100644 --- a/misoclib/mem/litesata/test/Makefile +++ b/misoclib/mem/litesata/test/Makefile @@ -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 index 00000000..4787df53 --- /dev/null +++ b/misoclib/mem/litesata/test/mirroring_tb.py @@ -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) -- 2.30.2