from lib.sata.transport import SATATransport
from lib.sata.command import SATACommand
+from lib.sata.frontend.crossbar import SATACrossbar
+
class SATACON(Module):
def __init__(self, phy):
###
+ # core
self.link = SATALink(phy)
self.transport = SATATransport(self.link)
self.command = SATACommand(self.transport)
- self.sink, self.source = self.command.sink, self.command.source
+ # frontend
+ self.crossbar = SATACrossbar(32)
+ self.comb += [
+ Record.connect(self.crossbar.master.source, self.command.sink),
+ Record.connect(self.command.source, self.crossbar.master.sink)
+ ]
from lib.sata.common import *
from lib.sata.link.scrambler import Scrambler
+
+from migen.fhdl.decorators import ModuleDecorator
from migen.bank.description import *
-class SATABIST(Module):
- def __init__(self, sata_con):
- self.write = Signal()
- self.read = Signal()
+class SATABISTGenerator(Module):
+ def __init__(self, sata_master_port):
+ self.start = Signal()
self.sector = Signal(48)
self.count = Signal(16)
self.loops = Signal(8)
+ self.random = Signal()
self.done = Signal()
- self.errors = Signal(32)
+ self.errors = Signal(32) # Note: Not used for writes
- ###
+ ###
- sink = sata_con.source
- source = sata_con.sink
+ source, sink = sata_master_port.source, sata_master_port.sink
self.counter = counter = Counter(bits_sign=32)
self.loops_counter = loops_counter = Counter(bits_sign=8)
- self.error_counter = Counter(self.errors, bits_sign=32)
self.scrambler = scrambler = InsertReset(Scrambler())
self.comb += [
self.done.eq(1),
counter.reset.eq(1),
loops_counter.reset.eq(1),
- If(self.write,
- self.error_counter.reset.eq(1),
- NextState("SEND_WRITE_CMD_AND_DATA")
- ).Elif(self.read,
- self.error_counter.reset.eq(1),
- NextState("SEND_READ_CMD")
+ If(self.start,
+ NextState("SEND_CMD_AND_DATA")
)
)
- fsm.act("SEND_WRITE_CMD_AND_DATA",
- source.stb.eq(1),
+ self.comb += [
source.sop.eq(counter.value == 0),
source.eop.eq(counter.value == (logical_sector_size//4*self.count)-1),
source.write.eq(1),
source.sector.eq(self.sector),
source.count.eq(self.count),
- source.data.eq(scrambler.value),
+ If(self.random,
+ source.data.eq(scrambler.value)
+ ).Else(
+ source.data.eq(counter.value)
+ )
+ ]
+ fsm.act("SEND_CMD_AND_DATA",
+ source.stb.eq(1),
If(source.stb & source.ack,
counter.ce.eq(1),
If(source.eop,
- NextState("WAIT_WRITE_ACK")
+ NextState("WAIT_ACK")
)
)
)
- fsm.act("WAIT_WRITE_ACK",
+ fsm.act("WAIT_ACK",
sink.ack.eq(1),
If(sink.stb,
loops_counter.ce.eq(1),
NextState("IDLE")
).Else(
counter.reset.eq(1),
- NextState("SEND_WRITE_CMD_AND_DATA")
+ NextState("SEND_CMD_AND_DATA")
)
)
)
- fsm.act("SEND_READ_CMD",
- source.stb.eq(1),
+
+class SATABISTChecker(Module):
+ def __init__(self, sata_master_port):
+ self.start = Signal()
+ self.sector = Signal(48)
+ self.count = Signal(16)
+ self.loops = Signal(8)
+ self.random = Signal()
+
+ self.done = Signal()
+ self.errors = Signal(32)
+
+ ###
+
+ source, sink = sata_master_port.source, sata_master_port.sink
+
+ self.counter = counter = Counter(bits_sign=32)
+ self.loops_counter = loops_counter = Counter(bits_sign=8)
+ self.error_counter = Counter(self.errors, bits_sign=32)
+
+ self.scrambler = scrambler = InsertReset(Scrambler())
+ self.comb += [
+ scrambler.reset.eq(counter.reset),
+ scrambler.ce.eq(counter.ce)
+ ]
+
+ self.fsm = fsm = FSM(reset_state="IDLE")
+ fsm.act("IDLE",
+ self.done.eq(1),
+ counter.reset.eq(1),
+ loops_counter.reset.eq(1),
+ If(self.start,
+ self.error_counter.reset.eq(1),
+ NextState("SEND_CMD")
+ )
+ )
+ self.comb += [
source.sop.eq(1),
source.eop.eq(1),
source.read.eq(1),
source.sector.eq(self.sector),
source.count.eq(self.count),
+ ]
+ fsm.act("SEND_CMD",
+ source.stb.eq(1),
If(source.ack,
counter.reset.eq(1),
- NextState("WAIT_READ_ACK")
+ NextState("WAIT_ACK")
)
)
- fsm.act("WAIT_READ_ACK",
+ fsm.act("WAIT_ACK",
If(sink.stb & sink.read,
- NextState("RECEIVE_READ_DATA")
+ NextState("RECEIVE_DATA")
)
)
- fsm.act("RECEIVE_READ_DATA",
+ expected_data = Signal(32)
+ self.comb += \
+ If(self.random,
+ expected_data.eq(scrambler.value)
+ ).Else(
+ expected_data.eq(counter.value)
+ )
+ fsm.act("RECEIVE_DATA",
sink.ack.eq(1),
If(sink.stb,
counter.ce.eq(1),
- If(sink.data != scrambler.value,
+ If(sink.data != expected_data,
self.error_counter.ce.eq(1)
),
If(sink.eop,
If(loops_counter.value == (self.loops-1),
NextState("IDLE")
).Else(
- NextState("SEND_READ_CMD")
+ NextState("SEND_CMD")
)
).Else(
- NextState("WAIT_READ_ACK")
+ NextState("WAIT_ACK")
)
)
)
)
class SATABISTControl(Module, AutoCSR):
- def __init__(self, sata_bist):
- self._write = CSR()
- self._read = CSR()
+ def __init__(self, bist_unit):
+ self._start = CSR()
self._sector = CSRStorage(48)
self._count = CSRStorage(16)
+ self._random = CSRStorage()
self._loops = CSRStorage(8)
-
self._done = CSRStatus()
self._errors = CSRStatus(32)
+ ###
+ self.bist_unit = bist_unit
self.comb += [
- sata_bist.write.eq(self._write.r & self._write.re),
- sata_bist.read.eq(self._read.r & self._read.re),
- sata_bist.sector.eq(self._sector.storage),
- sata_bist.count.eq(self._count.storage),
- sata_bist.loops.eq(self._loops.storage),
-
- self._done.status.eq(sata_bist.done),
- self._errors.status.eq(sata_bist.errors)
+ bist_unit.start.eq(self._start.r & self._start.re),
+ bist_unit.sector.eq(self._sector.storage),
+ bist_unit.count.eq(self._count.storage),
+ bist_unit.loops.eq(self._loops.storage),
+ bist_unit.random.eq(self._random.storage),
+
+ self._done.status.eq(bist_unit.done),
+ self._errors.status.eq(bist_unit.errors)
]
+
+class SATABIST(Module, AutoCSR):
+ def __init__(self, sata_master_ports, with_control=False):
+ generator = SATABISTGenerator(sata_master_ports[0])
+ checker = SATABISTChecker(sata_master_ports[1])
+ if with_control:
+ self.generator = SATABISTControl(generator)
+ self.checker = SATABISTControl(checker)
+ else:
+ self.generator = generator
+ self.checker = checker
transport.sink.count.eq(sink.count),
transport.sink.icc.eq(0),
transport.sink.control.eq(0),
+ transport.sink.data.eq(sink.data)
]
self.dwords_counter = dwords_counter = Counter(max=fis_max_dwords)
transport.sink.eop.eq((dwords_counter.value == (fis_max_dwords-1)) | sink.eop),
transport.sink.type.eq(fis_types["DATA"]),
- transport.sink.data.eq(sink.data),
sink.ack.eq(transport.sink.ack),
If(sink.stb & sink.ack,
If(sink.eop,
)
)
)
- fsm.act("PRESENT_READ_DATA",
- data_buffer.sink.stb.eq(transport.source.stb),
+ self.comb += [
data_buffer.sink.sop.eq(transport.source.sop),
data_buffer.sink.eop.eq(transport.source.eop),
- data_buffer.sink.data.eq(transport.source.data),
+ data_buffer.sink.data.eq(transport.source.data)
+ ]
+ fsm.act("PRESENT_READ_DATA",
+ data_buffer.sink.stb.eq(transport.source.stb),
transport.source.ack.eq(data_buffer.sink.ack),
If(data_buffer.sink.stb & data_buffer.sink.ack,
self.dwords_counter.ce.eq(~read_done),
source.last.eq(cmd_buffer.source.last),
source.success.eq(cmd_buffer.source.success),
source.failed.eq(cmd_buffer.source.success),
+ source.data.eq(data_buffer.source.data)
]
out_fsm.act("PRESENT_RESPONSE_WITH_DATA",
source.sop.eq(data_buffer.source.sop),
source.eop.eq(data_buffer.source.eop),
- source.data.eq(data_buffer.source.data),
data_buffer.source.ack.eq(source.ack),
If(source.stb & source.eop & source.ack,
self.width = flen(self.value)
self.sync += self.value.eq(self.value+1)
+# XXX use ModuleDecorator
class BufferizeEndpoints(Module):
def __init__(self, decorated, *args):
self.decorated = decorated
--- /dev/null
+from lib.sata.common import *
+from lib.sata.frontend.common import *
+
+from migen.genlib.roundrobin import *
+
+class SATAArbiter(Module):
+ def __init__(self, slaves, master):
+ if len(slaves) == 1:
+ self.comb += slaves[0].connect(master)
+ else:
+ self.rr = RoundRobin(len(slaves))
+ self.grant = self.rr.grant
+ cases = {}
+ for i, slave in enumerate(slaves):
+ sink, source = slave.sink, slave.source
+ start = Signal()
+ done = Signal()
+ ongoing = Signal()
+ self.comb += [
+ start.eq(sink.stb & sink.sop),
+ done.eq(source.stb & source.last & source.eop & source.ack)
+ ]
+ self.sync += \
+ If(start,
+ ongoing.eq(1)
+ ).Elif(done,
+ ongoing.eq(0)
+ )
+ self.comb += self.rr.request[i].eq((start | ongoing) & ~done)
+ cases[i] = [slaves[i].connect(master)]
+ self.comb += Case(self.grant, cases)
--- /dev/null
+from lib.sata.common import *
+
+class SATAMasterPort:
+ def __init__(self, dw):
+ self.source = Source(command_tx_description(dw))
+ self.sink = Sink(command_rx_description(dw))
+
+ def connect(self, slave):
+ return [
+ Record.connect(self.source, slave.sink),
+ Record.connect(slave.source, self.sink)
+ ]
+
+class SATASlavePort:
+ def __init__(self, dw):
+ self.sink = Sink(command_tx_description(dw))
+ self.source = Source(command_rx_description(dw))
+
+ def connect(self, master):
+ return [
+ Record.connect(self.sink, master.source),
+ Record.connect(master.sink, self.source)
+ ]
--- /dev/null
+from lib.sata.common import *
+from lib.sata.frontend.common import *
+from lib.sata.frontend.arbiter import SATAArbiter
+
+class SATACrossbar(Module):
+ def __init__(self, dw):
+ self.dw = dw
+ self.slaves = []
+ self.master = SATAMasterPort(dw)
+
+ def get_port(self):
+ master = SATAMasterPort(self.dw)
+ slave = SATASlavePort(self.dw)
+ self.comb += master.connect(slave)
+ self.slaves.append(slave)
+ return master
+
+ def get_ports(self, n):
+ masters = []
+ for i in range(n):
+ masters.append(self.get_port())
+ return masters
+
+ def do_finalize(self):
+ self.arbiter = SATAArbiter(self.slaves, self.master)
NextState("COPY")
)
)
+ self.comb += [
+ scrambler.sink.sop.eq(sop),
+ scrambler.sink.eop.eq(eop)
+ ]
fsm.act("COPY",
scrambler.sink.stb.eq(cont.source.stb & ((det == 0) | eop)),
- scrambler.sink.sop.eq(sop),
- scrambler.sink.eop.eq(eop),
insert.eq(primitives["R_IP"]),
If(det == primitives["HOLD"],
insert.eq(primitives["HOLDA"])
from lib.sata.common import *
from lib.sata import SATACON
-from lib.sata.bist import SATABIST
+from lib.sata.bist import SATABISTGenerator, SATABISTChecker
from lib.sata.test.hdd import *
from lib.sata.test.common import *
link_debug=False, link_random_level=0,
transport_debug=False, transport_loopback=False,
hdd_debug=True)
- self.con = SATACON(self.hdd.phy)
- self.bist = SATABIST(self.con)
+ self.controller = SATACON(self.hdd.phy)
+ self.generator = SATABISTGenerator(self.controller.crossbar.get_port())
+ self.checker = SATABISTChecker(self.controller.crossbar.get_port())
def gen_simulation(self, selfp):
hdd = self.hdd
hdd.malloc(0, 64)
- selfp.bist.sector = 0
- selfp.bist.count = 17
- selfp.bist.loops = 1
+ selfp.generator.sector = 0
+ selfp.generator.count = 17
+ selfp.checker.sector = 0
+ selfp.checker.count = 17
while True:
- selfp.bist.write = 1
+ selfp.generator.start = 1
yield
- selfp.bist.write = 0
+ selfp.generator.start = 0
yield
- while selfp.bist.done == 0:
+ while selfp.generator.done == 0:
yield
- selfp.bist.read = 1
+ selfp.checker.start = 1
yield
- selfp.bist.read = 0
+ selfp.checker.start = 0
yield
- while selfp.bist.done == 0:
+ while selfp.checker.done == 0:
yield
- print("errors {}".format(selfp.bist.errors))
- selfp.bist.sector += 1
- selfp.bist.count = max((selfp.bist.count + 1)%8, 1)
+ print("errors {}".format(selfp.checker.errors))
+ selfp.generator.sector += 1
+ selfp.generator.count = max((selfp.generator.count + 1)%8, 1)
+ selfp.checker.sector += 1
+ selfp.checker.count = max((selfp.checker.count + 1)%8, 1)
if __name__ == "__main__":
run_simulation(TB(), ncycles=8192*2, vcd_name="my.vcd", keep_files=True)
class TestDesign(UART2WB, AutoCSR):
default_platform = "kc705"
csr_map = {
- "sata_bist_ctrl": 10,
- "mila": 11
+ "sata_bist": 10,
+ "mila": 11
}
csr_map.update(UART2WB.csr_map)
self.sata_phy = SATAPHY(platform.request("sata_host"), clk_freq, speed="SATA2")
self.sata_con = SATACON(self.sata_phy)
- self.sata_bist = SATABIST(self.sata_con)
- self.sata_bist_ctrl = SATABISTControl(self.sata_bist)
+ self.sata_bist = SATABIST(self.sata_con.crossbar.get_ports(2), with_control=True)
self.leds = DebugLeds(platform, self.sata_phy)
self.sata_phy.sink.data,
self.sata_phy.sink.charisk,
- self.sata_con.sink.stb,
- self.sata_con.sink.sop,
- self.sata_con.sink.eop,
- self.sata_con.sink.ack,
- self.sata_con.sink.write,
- self.sata_con.sink.read,
-
- self.sata_con.source.stb,
- self.sata_con.source.sop,
- self.sata_con.source.eop,
- self.sata_con.source.ack,
- self.sata_con.source.write,
- self.sata_con.source.read,
- self.sata_con.source.success,
- self.sata_con.source.failed,
- self.sata_con.source.data
+ self.sata_con.command.sink.stb,
+ self.sata_con.command.sink.sop,
+ self.sata_con.command.sink.eop,
+ self.sata_con.command.sink.ack,
+ self.sata_con.command.sink.write,
+ self.sata_con.command.sink.read,
+
+ self.sata_con.command.source.stb,
+ self.sata_con.command.source.sop,
+ self.sata_con.command.source.eop,
+ self.sata_con.command.source.ack,
+ self.sata_con.command.source.write,
+ self.sata_con.command.source.read,
+ self.sata_con.command.source.success,
+ self.sata_con.command.source.failed,
+ self.sata_con.command.source.data
)
self.mila = MiLa(depth=2048, dat=Cat(*debug))
logical_sector_size = 512
class SATABISTDriver:
- def __init__(self, regs):
+ def __init__(self, regs, name):
self.regs = regs
+ self.name = name
+ for s in ["start", "sector", "count", "loops", "random", "done", "errors"]:
+ setattr(self, s, getattr(regs, name + "_"+ s))
- def run(self, sector, count, loops, mode):
- self.regs.sata_bist_ctrl_sector.write(sector)
- self.regs.sata_bist_ctrl_count.write(count)
- self.regs.sata_bist_ctrl_loops.write(loops)
- if mode == "write":
- self.regs.sata_bist_ctrl_write.write(1)
- elif mode == "read":
- self.regs.sata_bist_ctrl_read.write(1)
- while (self.regs.sata_bist_ctrl_done.read() == 0):
+ def run(self, sector, count, loops, random):
+ self.sector.write(sector)
+ self.count.write(count)
+ self.loops.write(loops)
+ self.random.write(random)
+ self.start.write(1)
+ while (self.done.read() == 0):
pass
- return self.regs.sata_bist_ctrl_errors.read()
+ return self.errors.read()
- def write(self, sector, count, loops):
- self.run(sector, count, loops, "write")
+class SATABISTGeneratorDriver(SATABISTDriver):
+ def __init__(self, regs, name):
+ SATABISTDriver.__init__(self, regs, name + "_generator")
- def read(self, sector, count, loops):
- return self.run(sector, count, loops, "read")
+class SATABISTCheckerDriver(SATABISTDriver):
+ def __init__(self, regs, name):
+ SATABISTDriver.__init__(self, regs, name + "_checker")
+
+class Timer:
+ def __init__(self):
+ self.value = None
+
+ def start(self):
+ self._start = time.time()
+
+ def stop(self):
+ self._stop = time.time()
+ self.value = self._stop - self._start
+
+KB = 1024
+MB = 1024*KB
+GB = 1024*MB
+
+def compute_speed(loops, count, elapsed_time, unit):
+ return loops*count*logical_sector_size/unit/elapsed_time
def _get_args():
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
description="""\
SATA BIST utility.
""")
- parser.add_argument("-s", "--sector", default=0, help="BIST start sector")
- parser.add_argument("-c", "--count", default=16384, help="BIST count (number of sectors per transaction)")
- parser.add_argument("-l", "--loops", default=4, help="BIST loops (number of loop for each transaction")
+ parser.add_argument("-s", "--sector", default=0, help="start sector")
+ parser.add_argument("-c", "--count", default=16384, help="number of sectors per transaction")
+ parser.add_argument("-l", "--loops", default=4, help="number of loop for each transaction")
+ parser.add_argument("-r", "--random", default=True, help="use random data")
return parser.parse_args()
args = _get_args()
wb.open()
###
- bist = SATABISTDriver(wb.regs)
+ generator = SATABISTGeneratorDriver(wb.regs, "sata_bist")
+ checker = SATABISTCheckerDriver(wb.regs, "sata_bist")
+ timer = Timer()
+
sector = int(args.sector)
count = int(args.count)
loops = int(args.loops)
+ random = int(args.random)
try:
- write_time = 0
- read_time = 0
while True:
- # Write
- start = time.time()
- bist.write(sector, count, loops)
- end = time.time()
- write_time = end-start
- write_speed = loops*count*logical_sector_size/(1024*1024)/write_time
-
- # Read
- start = time.time()
- read_errors = bist.read(sector, count, loops)
- end = time.time()
- read_time = end-start
- read_speed = loops*count*logical_sector_size/(1024*1024)/read_time
+ # generator (write data to HDD)
+ timer.start()
+ generator.run(sector, count, loops, random)
+ timer.stop()
+ write_speed = compute_speed(loops, count, timer.value, MB)
+ # checker (read and check data from HDD)
+ timer.start()
+ errors = checker.run(sector, count, loops, random)
+ timer.stop()
+ read_speed = compute_speed(loops, count, timer.value, MB)
sector += count
- print("sector=%d write_speed=%4.2fMB/sec read_speed=%4.2fMB/sec errors=%d" %(sector, write_speed, read_speed, read_errors))
+ print("sector=%d write_speed=%4.2fMB/sec read_speed=%4.2fMB/sec errors=%d" %(sector, write_speed, read_speed, errors))
except KeyboardInterrupt:
pass