add frontend and improve BIST

This commit is contained in:
Florent Kermarrec 2015-01-14 09:19:41 +01:00
parent 62f55e32cf
commit 788546c6ae
11 changed files with 311 additions and 136 deletions

View File

@ -3,11 +3,19 @@ from lib.sata.link import SATALink
from lib.sata.transport import SATATransport from lib.sata.transport import SATATransport
from lib.sata.command import SATACommand from lib.sata.command import SATACommand
from lib.sata.frontend.crossbar import SATACrossbar
class SATACON(Module): class SATACON(Module):
def __init__(self, phy): def __init__(self, phy):
### ###
# core
self.link = SATALink(phy) self.link = SATALink(phy)
self.transport = SATATransport(self.link) self.transport = SATATransport(self.link)
self.command = SATACommand(self.transport) 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)
]

View File

@ -1,22 +1,90 @@
from lib.sata.common import * from lib.sata.common import *
from lib.sata.link.scrambler import Scrambler from lib.sata.link.scrambler import Scrambler
from migen.fhdl.decorators import ModuleDecorator
from migen.bank.description import * from migen.bank.description import *
class SATABIST(Module): class SATABISTGenerator(Module):
def __init__(self, sata_con): def __init__(self, sata_master_port):
self.write = Signal() self.start = Signal()
self.read = Signal()
self.sector = Signal(48) self.sector = Signal(48)
self.count = Signal(16) self.count = Signal(16)
self.loops = Signal(8) self.loops = Signal(8)
self.random = Signal()
self.done = Signal()
self.errors = Signal(32) # Note: Not used for writes
###
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.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,
NextState("SEND_CMD_AND_DATA")
)
)
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),
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_ACK")
)
)
)
fsm.act("WAIT_ACK",
sink.ack.eq(1),
If(sink.stb,
loops_counter.ce.eq(1),
If(loops_counter.value == (self.loops-1),
NextState("IDLE")
).Else(
counter.reset.eq(1),
NextState("SEND_CMD_AND_DATA")
)
)
)
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.done = Signal()
self.errors = Signal(32) self.errors = Signal(32)
### ###
sink = sata_con.source source, sink = sata_master_port.source, sata_master_port.sink
source = sata_con.sink
self.counter = counter = Counter(bits_sign=32) self.counter = counter = Counter(bits_sign=32)
self.loops_counter = loops_counter = Counter(bits_sign=8) self.loops_counter = loops_counter = Counter(bits_sign=8)
@ -33,63 +101,42 @@ class SATABIST(Module):
self.done.eq(1), self.done.eq(1),
counter.reset.eq(1), counter.reset.eq(1),
loops_counter.reset.eq(1), loops_counter.reset.eq(1),
If(self.write, If(self.start,
self.error_counter.reset.eq(1), self.error_counter.reset.eq(1),
NextState("SEND_WRITE_CMD_AND_DATA") NextState("SEND_CMD")
).Elif(self.read,
self.error_counter.reset.eq(1),
NextState("SEND_READ_CMD")
) )
) )
fsm.act("SEND_WRITE_CMD_AND_DATA", self.comb += [
source.stb.eq(1),
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(source.stb & source.ack,
counter.ce.eq(1),
If(source.eop,
NextState("WAIT_WRITE_ACK")
)
)
)
fsm.act("WAIT_WRITE_ACK",
sink.ack.eq(1),
If(sink.stb,
loops_counter.ce.eq(1),
If(loops_counter.value == (self.loops-1),
NextState("IDLE")
).Else(
counter.reset.eq(1),
NextState("SEND_WRITE_CMD_AND_DATA")
)
)
)
fsm.act("SEND_READ_CMD",
source.stb.eq(1),
source.sop.eq(1), source.sop.eq(1),
source.eop.eq(1), source.eop.eq(1),
source.read.eq(1), source.read.eq(1),
source.sector.eq(self.sector), source.sector.eq(self.sector),
source.count.eq(self.count), source.count.eq(self.count),
]
fsm.act("SEND_CMD",
source.stb.eq(1),
If(source.ack, If(source.ack,
counter.reset.eq(1), 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, 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), sink.ack.eq(1),
If(sink.stb, If(sink.stb,
counter.ce.eq(1), counter.ce.eq(1),
If(sink.data != scrambler.value, If(sink.data != expected_data,
self.error_counter.ce.eq(1) self.error_counter.ce.eq(1)
), ),
If(sink.eop, If(sink.eop,
@ -98,33 +145,45 @@ class SATABIST(Module):
If(loops_counter.value == (self.loops-1), If(loops_counter.value == (self.loops-1),
NextState("IDLE") NextState("IDLE")
).Else( ).Else(
NextState("SEND_READ_CMD") NextState("SEND_CMD")
) )
).Else( ).Else(
NextState("WAIT_READ_ACK") NextState("WAIT_ACK")
) )
) )
) )
) )
class SATABISTControl(Module, AutoCSR): class SATABISTControl(Module, AutoCSR):
def __init__(self, sata_bist): def __init__(self, bist_unit):
self._write = CSR() self._start = CSR()
self._read = CSR()
self._sector = CSRStorage(48) self._sector = CSRStorage(48)
self._count = CSRStorage(16) self._count = CSRStorage(16)
self._random = CSRStorage()
self._loops = CSRStorage(8) self._loops = CSRStorage(8)
self._done = CSRStatus() self._done = CSRStatus()
self._errors = CSRStatus(32) self._errors = CSRStatus(32)
###
self.bist_unit = bist_unit
self.comb += [ self.comb += [
sata_bist.write.eq(self._write.r & self._write.re), bist_unit.start.eq(self._start.r & self._start.re),
sata_bist.read.eq(self._read.r & self._read.re), bist_unit.sector.eq(self._sector.storage),
sata_bist.sector.eq(self._sector.storage), bist_unit.count.eq(self._count.storage),
sata_bist.count.eq(self._count.storage), bist_unit.loops.eq(self._loops.storage),
sata_bist.loops.eq(self._loops.storage), bist_unit.random.eq(self._random.storage),
self._done.status.eq(sata_bist.done), self._done.status.eq(bist_unit.done),
self._errors.status.eq(sata_bist.errors) 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

View File

@ -26,6 +26,7 @@ class SATACommandTX(Module):
transport.sink.count.eq(sink.count), transport.sink.count.eq(sink.count),
transport.sink.icc.eq(0), transport.sink.icc.eq(0),
transport.sink.control.eq(0), transport.sink.control.eq(0),
transport.sink.data.eq(sink.data)
] ]
self.dwords_counter = dwords_counter = Counter(max=fis_max_dwords) self.dwords_counter = dwords_counter = Counter(max=fis_max_dwords)
@ -70,7 +71,6 @@ class SATACommandTX(Module):
transport.sink.eop.eq((dwords_counter.value == (fis_max_dwords-1)) | sink.eop), transport.sink.eop.eq((dwords_counter.value == (fis_max_dwords-1)) | sink.eop),
transport.sink.type.eq(fis_types["DATA"]), transport.sink.type.eq(fis_types["DATA"]),
transport.sink.data.eq(sink.data),
sink.ack.eq(transport.sink.ack), sink.ack.eq(transport.sink.ack),
If(sink.stb & sink.ack, If(sink.stb & sink.ack,
If(sink.eop, If(sink.eop,
@ -171,11 +171,13 @@ class SATACommandRX(Module):
) )
) )
) )
fsm.act("PRESENT_READ_DATA", self.comb += [
data_buffer.sink.stb.eq(transport.source.stb),
data_buffer.sink.sop.eq(transport.source.sop), data_buffer.sink.sop.eq(transport.source.sop),
data_buffer.sink.eop.eq(transport.source.eop), 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), transport.source.ack.eq(data_buffer.sink.ack),
If(data_buffer.sink.stb & data_buffer.sink.ack, If(data_buffer.sink.stb & data_buffer.sink.ack,
self.dwords_counter.ce.eq(~read_done), self.dwords_counter.ce.eq(~read_done),
@ -230,6 +232,7 @@ class SATACommandRX(Module):
source.last.eq(cmd_buffer.source.last), source.last.eq(cmd_buffer.source.last),
source.success.eq(cmd_buffer.source.success), source.success.eq(cmd_buffer.source.success),
source.failed.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", out_fsm.act("PRESENT_RESPONSE_WITH_DATA",
@ -237,7 +240,6 @@ class SATACommandRX(Module):
source.sop.eq(data_buffer.source.sop), source.sop.eq(data_buffer.source.sop),
source.eop.eq(data_buffer.source.eop), source.eop.eq(data_buffer.source.eop),
source.data.eq(data_buffer.source.data),
data_buffer.source.ack.eq(source.ack), data_buffer.source.ack.eq(source.ack),
If(source.stb & source.eop & source.ack, If(source.stb & source.eop & source.ack,

View File

@ -210,6 +210,7 @@ class Counter(Module):
self.width = flen(self.value) self.width = flen(self.value)
self.sync += self.value.eq(self.value+1) self.sync += self.value.eq(self.value+1)
# XXX use ModuleDecorator
class BufferizeEndpoints(Module): class BufferizeEndpoints(Module):
def __init__(self, decorated, *args): def __init__(self, decorated, *args):
self.decorated = decorated self.decorated = decorated

View File

@ -0,0 +1,31 @@
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)

View File

@ -0,0 +1,23 @@
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)
]

View File

@ -0,0 +1,25 @@
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)

View File

@ -187,10 +187,12 @@ class SATALinkRX(Module):
NextState("COPY") NextState("COPY")
) )
) )
self.comb += [
scrambler.sink.sop.eq(sop),
scrambler.sink.eop.eq(eop)
]
fsm.act("COPY", fsm.act("COPY",
scrambler.sink.stb.eq(cont.source.stb & ((det == 0) | eop)), 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"]), insert.eq(primitives["R_IP"]),
If(det == primitives["HOLD"], If(det == primitives["HOLD"],
insert.eq(primitives["HOLDA"]) insert.eq(primitives["HOLDA"])

View File

@ -1,6 +1,6 @@
from lib.sata.common import * from lib.sata.common import *
from lib.sata import SATACON 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.hdd import *
from lib.sata.test.common import * from lib.sata.test.common import *
@ -11,31 +11,35 @@ class TB(Module):
link_debug=False, link_random_level=0, link_debug=False, link_random_level=0,
transport_debug=False, transport_loopback=False, transport_debug=False, transport_loopback=False,
hdd_debug=True) hdd_debug=True)
self.con = SATACON(self.hdd.phy) self.controller = SATACON(self.hdd.phy)
self.bist = SATABIST(self.con) self.generator = SATABISTGenerator(self.controller.crossbar.get_port())
self.checker = SATABISTChecker(self.controller.crossbar.get_port())
def gen_simulation(self, selfp): def gen_simulation(self, selfp):
hdd = self.hdd hdd = self.hdd
hdd.malloc(0, 64) hdd.malloc(0, 64)
selfp.bist.sector = 0 selfp.generator.sector = 0
selfp.bist.count = 17 selfp.generator.count = 17
selfp.bist.loops = 1 selfp.checker.sector = 0
selfp.checker.count = 17
while True: while True:
selfp.bist.write = 1 selfp.generator.start = 1
yield yield
selfp.bist.write = 0 selfp.generator.start = 0
yield yield
while selfp.bist.done == 0: while selfp.generator.done == 0:
yield yield
selfp.bist.read = 1 selfp.checker.start = 1
yield yield
selfp.bist.read = 0 selfp.checker.start = 0
yield yield
while selfp.bist.done == 0: while selfp.checker.done == 0:
yield yield
print("errors {}".format(selfp.bist.errors)) print("errors {}".format(selfp.checker.errors))
selfp.bist.sector += 1 selfp.generator.sector += 1
selfp.bist.count = max((selfp.bist.count + 1)%8, 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__": if __name__ == "__main__":
run_simulation(TB(), ncycles=8192*2, vcd_name="my.vcd", keep_files=True) run_simulation(TB(), ncycles=8192*2, vcd_name="my.vcd", keep_files=True)

View File

@ -159,8 +159,8 @@ class DebugLeds(Module):
class TestDesign(UART2WB, AutoCSR): class TestDesign(UART2WB, AutoCSR):
default_platform = "kc705" default_platform = "kc705"
csr_map = { csr_map = {
"sata_bist_ctrl": 10, "sata_bist": 10,
"mila": 11 "mila": 11
} }
csr_map.update(UART2WB.csr_map) csr_map.update(UART2WB.csr_map)
@ -171,9 +171,8 @@ class TestDesign(UART2WB, AutoCSR):
self.sata_phy = SATAPHY(platform.request("sata_host"), clk_freq, speed="SATA2") self.sata_phy = SATAPHY(platform.request("sata_host"), clk_freq, speed="SATA2")
self.sata_con = SATACON(self.sata_phy) 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.leds = DebugLeds(platform, self.sata_phy)
@ -189,22 +188,22 @@ class TestDesign(UART2WB, AutoCSR):
self.sata_phy.sink.data, self.sata_phy.sink.data,
self.sata_phy.sink.charisk, self.sata_phy.sink.charisk,
self.sata_con.sink.stb, self.sata_con.command.sink.stb,
self.sata_con.sink.sop, self.sata_con.command.sink.sop,
self.sata_con.sink.eop, self.sata_con.command.sink.eop,
self.sata_con.sink.ack, self.sata_con.command.sink.ack,
self.sata_con.sink.write, self.sata_con.command.sink.write,
self.sata_con.sink.read, self.sata_con.command.sink.read,
self.sata_con.source.stb, self.sata_con.command.source.stb,
self.sata_con.source.sop, self.sata_con.command.source.sop,
self.sata_con.source.eop, self.sata_con.command.source.eop,
self.sata_con.source.ack, self.sata_con.command.source.ack,
self.sata_con.source.write, self.sata_con.command.source.write,
self.sata_con.source.read, self.sata_con.command.source.read,
self.sata_con.source.success, self.sata_con.command.source.success,
self.sata_con.source.failed, self.sata_con.command.source.failed,
self.sata_con.source.data self.sata_con.command.source.data
) )
self.mila = MiLa(depth=2048, dat=Cat(*debug)) self.mila = MiLa(depth=2048, dat=Cat(*debug))

View File

@ -5,35 +5,57 @@ from config import *
logical_sector_size = 512 logical_sector_size = 512
class SATABISTDriver: class SATABISTDriver:
def __init__(self, regs): def __init__(self, regs, name):
self.regs = regs 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): def run(self, sector, count, loops, random):
self.regs.sata_bist_ctrl_sector.write(sector) self.sector.write(sector)
self.regs.sata_bist_ctrl_count.write(count) self.count.write(count)
self.regs.sata_bist_ctrl_loops.write(loops) self.loops.write(loops)
if mode == "write": self.random.write(random)
self.regs.sata_bist_ctrl_write.write(1) self.start.write(1)
elif mode == "read": while (self.done.read() == 0):
self.regs.sata_bist_ctrl_read.write(1)
while (self.regs.sata_bist_ctrl_done.read() == 0):
pass pass
return self.regs.sata_bist_ctrl_errors.read() return self.errors.read()
def write(self, sector, count, loops): class SATABISTGeneratorDriver(SATABISTDriver):
self.run(sector, count, loops, "write") def __init__(self, regs, name):
SATABISTDriver.__init__(self, regs, name + "_generator")
def read(self, sector, count, loops): class SATABISTCheckerDriver(SATABISTDriver):
return self.run(sector, count, loops, "read") 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(): def _get_args():
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
description="""\ description="""\
SATA BIST utility. SATA BIST utility.
""") """)
parser.add_argument("-s", "--sector", default=0, help="BIST start sector") parser.add_argument("-s", "--sector", default=0, help="start sector")
parser.add_argument("-c", "--count", default=16384, help="BIST count (number of sectors per transaction)") parser.add_argument("-c", "--count", default=16384, help="number of sectors per transaction")
parser.add_argument("-l", "--loops", default=4, help="BIST loops (number of loop for each 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() return parser.parse_args()
@ -41,31 +63,30 @@ if __name__ == "__main__":
args = _get_args() args = _get_args()
wb.open() wb.open()
### ###
bist = SATABISTDriver(wb.regs) generator = SATABISTGeneratorDriver(wb.regs, "sata_bist")
checker = SATABISTCheckerDriver(wb.regs, "sata_bist")
timer = Timer()
sector = int(args.sector) sector = int(args.sector)
count = int(args.count) count = int(args.count)
loops = int(args.loops) loops = int(args.loops)
random = int(args.random)
try: try:
write_time = 0
read_time = 0
while True: while True:
# Write # generator (write data to HDD)
start = time.time() timer.start()
bist.write(sector, count, loops) generator.run(sector, count, loops, random)
end = time.time() timer.stop()
write_time = end-start write_speed = compute_speed(loops, count, timer.value, MB)
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
# 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 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: except KeyboardInterrupt:
pass pass