diff --git a/lib/sata/__init__.py b/lib/sata/__init__.py index 34c7a3534..0a167bdc7 100644 --- a/lib/sata/__init__.py +++ b/lib/sata/__init__.py @@ -3,11 +3,19 @@ from lib.sata.link import SATALink 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) + ] diff --git a/lib/sata/bist.py b/lib/sata/bist.py index 4948022fd..5de8e7c1a 100644 --- a/lib/sata/bist.py +++ b/lib/sata/bist.py @@ -1,22 +1,90 @@ 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) # 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.errors = Signal(32) - ### + ### - 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) @@ -33,63 +101,42 @@ class SATABIST(Module): self.done.eq(1), counter.reset.eq(1), loops_counter.reset.eq(1), - If(self.write, + If(self.start, 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") + NextState("SEND_CMD") ) ) - fsm.act("SEND_WRITE_CMD_AND_DATA", - 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), + 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, @@ -98,33 +145,45 @@ class SATABIST(Module): 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), + 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(sata_bist.done), - self._errors.status.eq(sata_bist.errors) + 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 diff --git a/lib/sata/command/__init__.py b/lib/sata/command/__init__.py index dafc535b2..8b2ce5b8c 100644 --- a/lib/sata/command/__init__.py +++ b/lib/sata/command/__init__.py @@ -26,6 +26,7 @@ class SATACommandTX(Module): 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) @@ -70,7 +71,6 @@ class SATACommandTX(Module): 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, @@ -171,11 +171,13 @@ class SATACommandRX(Module): ) ) ) - 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), @@ -230,6 +232,7 @@ class SATACommandRX(Module): 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", @@ -237,7 +240,6 @@ class SATACommandRX(Module): 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, diff --git a/lib/sata/common.py b/lib/sata/common.py index ecb6723a3..32cabdc7b 100644 --- a/lib/sata/common.py +++ b/lib/sata/common.py @@ -210,6 +210,7 @@ class Counter(Module): 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 diff --git a/lib/sata/frontend/arbiter.py b/lib/sata/frontend/arbiter.py new file mode 100644 index 000000000..678f2d7a2 --- /dev/null +++ b/lib/sata/frontend/arbiter.py @@ -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) diff --git a/lib/sata/frontend/common.py b/lib/sata/frontend/common.py new file mode 100644 index 000000000..114a0a834 --- /dev/null +++ b/lib/sata/frontend/common.py @@ -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) + ] diff --git a/lib/sata/frontend/crossbar.py b/lib/sata/frontend/crossbar.py new file mode 100644 index 000000000..41ac806e8 --- /dev/null +++ b/lib/sata/frontend/crossbar.py @@ -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) diff --git a/lib/sata/link/__init__.py b/lib/sata/link/__init__.py index 381bba88b..b142e68cb 100644 --- a/lib/sata/link/__init__.py +++ b/lib/sata/link/__init__.py @@ -187,10 +187,12 @@ class SATALinkRX(Module): 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"]) diff --git a/lib/sata/test/bist_tb.py b/lib/sata/test/bist_tb.py index 064767377..72c99979f 100644 --- a/lib/sata/test/bist_tb.py +++ b/lib/sata/test/bist_tb.py @@ -1,6 +1,6 @@ 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 * @@ -11,31 +11,35 @@ class TB(Module): 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) diff --git a/targets/test.py b/targets/test.py index 850086ee3..e186ba8c6 100644 --- a/targets/test.py +++ b/targets/test.py @@ -159,8 +159,8 @@ class DebugLeds(Module): 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) @@ -171,9 +171,8 @@ class TestDesign(UART2WB, AutoCSR): 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) @@ -189,22 +188,22 @@ class TestDesign(UART2WB, AutoCSR): 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.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.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.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)) diff --git a/test/bist.py b/test/bist.py index 86d116a2e..82d11a726 100644 --- a/test/bist.py +++ b/test/bist.py @@ -5,35 +5,57 @@ from config import * 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() @@ -41,31 +63,30 @@ if __name__ == "__main__": 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