litesata: add striping module for use of multiple HDDs.

This commit is contained in:
Florent Kermarrec 2015-05-23 14:12:20 +02:00
parent 5daba9af68
commit 1bb5a05488
4 changed files with 203 additions and 0 deletions

View File

@ -1,4 +1,5 @@
from misoclib.mem.litesata.common import * from misoclib.mem.litesata.common import *
from misoclib.mem.litesata.frontend.crossbar import LiteSATACrossbar from misoclib.mem.litesata.frontend.crossbar import LiteSATACrossbar
from misoclib.mem.litesata.frontend.arbiter import LiteSATAArbiter from misoclib.mem.litesata.frontend.arbiter import LiteSATAArbiter
from misoclib.mem.litesata.frontend.striping import LiteSATAStriping
from misoclib.mem.litesata.frontend.bist import LiteSATABIST from misoclib.mem.litesata.frontend.bist import LiteSATABIST

View File

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

View File

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

View File

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