litesata: add mirroring

This commit is contained in:
Florent Kermarrec 2015-05-25 14:03:14 +02:00
parent c3716296ae
commit 0d1a7b9315
5 changed files with 402 additions and 0 deletions

View file

@ -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

View file

@ -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()

View file

@ -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)
]

View file

@ -32,5 +32,8 @@ bist_tb:
striping_tb:
$(CMD) striping_tb.py
mirroring_tb:
$(CMD) mirroring_tb.py
clean:
rm crc scrambler *.vcd

View file

@ -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)