litesata: add striping module for use of multiple HDDs.
This commit is contained in:
parent
5daba9af68
commit
1bb5a05488
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue