command: add support for larger DMAs
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Tue, 6 Jan 2015 15:48:19 +0000 (16:48 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Tue, 6 Jan 2015 15:48:19 +0000 (16:48 +0100)
lib/sata/__init__.py
lib/sata/bist.py
lib/sata/command/__init__.py
lib/sata/common.py
lib/sata/test/bist_tb.py
lib/sata/test/command_tb.py
lib/sata/test/hdd.py
test/bist.py

index de72c7ef440e07e772a84a738ae340f08224df95..34c7a3534b7c3f237402decc96444b4836cba949 100644 (file)
@@ -4,14 +4,10 @@ from lib.sata.transport import SATATransport
 from lib.sata.command import SATACommand
 
 class SATACON(Module):
-       def __init__(self, phy, sector_size=512, max_count=16):
-               self.sector_size = sector_size
-               self.max_count = max_count
-
+       def __init__(self, phy):
                ###
-
                self.link = SATALink(phy)
                self.transport = SATATransport(self.link)
-               self.command = SATACommand(self.transport, sector_size, max_count)
+               self.command = SATACommand(self.transport)
                self.sink, self.source = self.command.sink, self.command.source
 
index 1ee2f001f91e946d00044a54e0a3a0c10f6adbd0..73b14ac5277271ee08729a8d351818ee4ccdf282 100644 (file)
@@ -11,7 +11,7 @@ class SATABISTUnit(Module):
                self.write_only = Signal()
                self.read_only = Signal()
                self.sector = Signal(48)
-               self.count = Signal(4)
+               self.count = Signal(16)
                self.done = Signal()
                self.ctrl_errors = Signal(32)
                self.data_errors = Signal(32)
@@ -43,7 +43,7 @@ class SATABISTUnit(Module):
                fsm.act("SEND_WRITE_CMD_AND_DATA",
                        source.stb.eq(1),
                        source.sop.eq((counter.value == 0)),
-                       source.eop.eq((counter.value == (sata_con.sector_size//4*self.count)-1)),
+                       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),
@@ -74,11 +74,11 @@ class SATABISTUnit(Module):
                        source.sector.eq(self.sector),
                        source.count.eq(self.count),
                        If(source.ack,
+                               counter.reset.eq(1),
                                NextState("WAIT_READ_ACK")
                        )
                )
                fsm.act("WAIT_READ_ACK",
-                       counter.reset.eq(1),
                        If(sink.stb & sink.read,
                                If(~sink.read | ~sink.success | sink.failed,
                                        self.ctrl_error_counter.ce.eq(1)
@@ -94,7 +94,11 @@ class SATABISTUnit(Module):
                                        self.data_error_counter.ce.eq(1)
                                ),
                                If(sink.eop,
-                                       NextState("IDLE")
+                                       If(sink.last,
+                                               NextState("IDLE")
+                                       ).Else(
+                                               NextState("WAIT_READ_ACK")
+                                       )
                                )
                        )
                )
index 42ba8931bc7dce7441f9d6c42026762511d0863c..ba2297c7e23351494a0ff9c457b03bdf254f088d 100644 (file)
@@ -2,7 +2,8 @@ from lib.sata.common import *
 
 tx_to_rx = [
        ("write", 1),
-       ("read", 1)
+       ("read", 1),
+       ("count", 16)
 ]
 
 rx_to_tx = [
@@ -10,7 +11,7 @@ rx_to_tx = [
 ]
 
 class SATACommandTX(Module):
-       def __init__(self, transport, sector_size):
+       def __init__(self, transport):
                self.sink = sink = Sink(command_tx_description(32))
                self.to_rx = to_rx = Source(tx_to_rx)
                self.from_rx = from_rx = Sink(rx_to_tx)
@@ -27,6 +28,8 @@ class SATACommandTX(Module):
                        transport.sink.control.eq(0),
                ]
 
+               self.dwords_counter = dwords_counter = Counter(max=fis_max_dwords)
+
                self.fsm = fsm = FSM(reset_state="IDLE")
                fsm.act("IDLE",
                        sink.ack.eq(0),
@@ -54,19 +57,27 @@ class SATACommandTX(Module):
                        )
                )
                fsm.act("WAIT_DMA_ACTIVATE",
+                       dwords_counter.reset.eq(1),
                        If(from_rx.dma_activate,
                                NextState("SEND_DATA")
                        )
                )
                fsm.act("SEND_DATA",
+                       dwords_counter.ce.eq(sink.stb & sink.ack),
+
                        transport.sink.stb.eq(sink.stb),
-                       transport.sink.sop.eq(sink.sop),
-                       transport.sink.eop.eq(sink.eop),
+                       transport.sink.sop.eq(dwords_counter.value == 0),
+                       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 & sink.eop,
-                               NextState("IDLE")
+                       If(sink.stb & sink.ack,
+                               If(sink.eop,
+                                       NextState("IDLE")
+                               ).Elif(dwords_counter.value == (fis_max_dwords-1),
+                                       NextState("WAIT_DMA_ACTIVATE")
+                               )
                        )
                )
                fsm.act("SEND_READ_DMA_CMD",
@@ -85,59 +96,72 @@ class SATACommandTX(Module):
                self.comb += [
                        If(sink.stb,
                                to_rx.write.eq(sink.write),
-                               to_rx.read.eq(sink.read)
+                               to_rx.read.eq(sink.read),
+                               to_rx.count.eq(sink.count)
                        )
                ]
 
 class SATACommandRX(Module):
-       def __init__(self, transport, sector_size, max_count):
+       def __init__(self, transport):
                self.source = source = Source(command_rx_description(32))
                self.to_tx = to_tx = Source(rx_to_tx)
                self.from_tx = from_tx = Sink(tx_to_rx)
 
                ###
 
-               cmd_fifo = SyncFIFO(command_rx_cmd_description(32), 2) # Note: ideally depth=1
-               data_fifo = InsertReset(SyncFIFO(command_rx_data_description(32), (sector_size*max_count//4), buffered=True))
+               cmd_fifo = SyncFIFO(command_rx_cmd_description(32), 2) # Note: ideally depth of 1
+               # XXX Simulate a fifo with depth of 1, FIXME
+               cmd_fifo_sink_stb = Signal()
+               cmd_fifo_sink_ack = Signal()
+               self.comb += [
+                       cmd_fifo.sink.stb.eq(cmd_fifo_sink_stb & ~cmd_fifo.fifo.readable),
+                       cmd_fifo_sink_ack.eq(~cmd_fifo.fifo.readable)
+               ]
+               data_fifo = InsertReset(SyncFIFO(command_rx_data_description(32), fis_max_dwords, buffered=True))
                self.submodules += cmd_fifo, data_fifo
 
                def test_type(name):
                        return transport.source.type == fis_types[name]
 
                dma_activate = Signal()
+               read_ndwords = Signal(max=sectors2dwords(2**16))
+               self.dwords_counter = dwords_counter = Counter(max=sectors2dwords(2**16))
+               read_done = Signal()
+
+               self.sync += \
+                       If(from_tx.read,
+                               read_ndwords.eq(from_tx.count*sectors2dwords(1)-1)
+                       )
+               self.comb += read_done.eq(self.dwords_counter.value == read_ndwords)
 
                self.fsm = fsm = FSM(reset_state="IDLE")
                fsm.act("IDLE",
+                       self.dwords_counter.reset.eq(1),
                        transport.source.ack.eq(1),
                        If(from_tx.write,
-                               NextState("WAIT_WRITE_ACTIVATE")
+                               NextState("WAIT_WRITE_ACTIVATE_OR_REG_D2H")
                        ).Elif(from_tx.read,
                                NextState("WAIT_READ_DATA")
                        )
                )
-               fsm.act("WAIT_WRITE_ACTIVATE",
+               fsm.act("WAIT_WRITE_ACTIVATE_OR_REG_D2H",
+                       # XXX: use status and error fields of REG_D2H
                        transport.source.ack.eq(1),
                        If(transport.source.stb,
                                If(test_type("DMA_ACTIVATE_D2H"),
                                        dma_activate.eq(1),
-                                       NextState("WAIT_WRITE_REG_D2H")
-                               )
-                       )
-               )
-               fsm.act("WAIT_WRITE_REG_D2H",
-                       transport.source.ack.eq(1),
-                       If(transport.source.stb,
-                               If(test_type("REG_D2H"),
+                               ).Elif(test_type("REG_D2H"),
                                        NextState("PRESENT_WRITE_RESPONSE")
                                )
                        )
                )
                fsm.act("PRESENT_WRITE_RESPONSE",
-                       cmd_fifo.sink.stb.eq(1),
+                       cmd_fifo_sink_stb.eq(1),
                        cmd_fifo.sink.write.eq(1),
+                       cmd_fifo.sink.last.eq(1),
                        cmd_fifo.sink.success.eq(~transport.source.error),
                        cmd_fifo.sink.failed.eq(transport.source.error),
-                       If(cmd_fifo.sink.stb & cmd_fifo.sink.ack,
+                       If(cmd_fifo_sink_stb & cmd_fifo_sink_ack,
                                NextState("IDLE")
                        )
                )
@@ -156,18 +180,26 @@ class SATACommandRX(Module):
                        data_fifo.sink.eop.eq(transport.source.eop),
                        data_fifo.sink.data.eq(transport.source.data),
                        transport.source.ack.eq(data_fifo.sink.ack),
-                       If(data_fifo.sink.stb & data_fifo.sink.eop & data_fifo.sink.ack,
-                               NextState("WAIT_READ_REG_D2H")
+                       If(data_fifo.sink.stb & data_fifo.sink.ack,
+                               self.dwords_counter.ce.eq(~read_done),
+                               If(data_fifo.sink.eop,
+                                       If(read_done,
+                                               NextState("WAIT_READ_REG_D2H")
+                                       ).Else(
+                                               NextState("PRESENT_READ_RESPONSE")
+                                       )
+                               )
                        )
                )
                read_error = Signal()
                self.sync += \
-                       If(fsm.ongoing("IDLE"),
+                       If(fsm.ongoing("WAIT_READ_DATA"),
                                read_error.eq(1)
                        ).Elif(transport.source.stb & transport.source.ack & transport.source.eop,
                                read_error.eq(transport.source.error)
                        )
                fsm.act("WAIT_READ_REG_D2H",
+                       # XXX: use status and error fields of REG_D2H
                        transport.source.ack.eq(1),
                        If(transport.source.stb,
                                If(test_type("REG_D2H"),
@@ -176,16 +208,19 @@ class SATACommandRX(Module):
                        )
                )
                fsm.act("PRESENT_READ_RESPONSE",
-                       cmd_fifo.sink.stb.eq(1),
+                       cmd_fifo_sink_stb.eq(1),
                        cmd_fifo.sink.read.eq(1),
+                       cmd_fifo.sink.last.eq(read_done),
                        cmd_fifo.sink.success.eq(~read_error),
                        cmd_fifo.sink.failed.eq(read_error),
-                       If(~cmd_fifo.fifo.readable, # Note: simulate a fifo with depth=1
-                               If(cmd_fifo.sink.stb & cmd_fifo.sink.ack,
-                                       If(cmd_fifo.sink.failed,
-                                               data_fifo.reset.eq(1)
-                                       ),
+                       If(cmd_fifo_sink_stb & cmd_fifo_sink_ack,
+                               If(cmd_fifo.sink.failed,
+                                       data_fifo.reset.eq(1)
+                               ),
+                               If(read_done,
                                        NextState("IDLE")
+                               ).Else(
+                                       NextState("WAIT_READ_DATA")
                                )
                        )
                )
@@ -202,11 +237,13 @@ class SATACommandRX(Module):
                                )
                        )
                )
+               # XXX try to merge PRESENT_XXX states
                out_fsm.act("PRESENT_WRITE_RESPONSE",
                        source.stb.eq(1),
                        source.sop.eq(1),
                        source.eop.eq(1),
                        source.write.eq(1),
+                       source.last.eq(cmd_fifo.source.last),
                        source.success.eq(cmd_fifo.source.success),
                        If(source.stb & source.ack,
                                cmd_fifo.source.ack.eq(1),
@@ -217,6 +254,7 @@ class SATACommandRX(Module):
                        source.stb.eq(data_fifo.source.stb),
                        source.read.eq(cmd_fifo.source.read),
                        source.success.eq(1),
+                       source.last.eq(cmd_fifo.source.last),
                        source.sop.eq(data_fifo.source.sop),
                        source.eop.eq(data_fifo.source.eop),
                        source.data.eq(data_fifo.source.data),
@@ -231,6 +269,7 @@ class SATACommandRX(Module):
                        source.sop.eq(1),
                        source.eop.eq(1),
                        source.read.eq(cmd_fifo.source.read),
+                       source.last.eq(cmd_fifo.source.last),
                        source.failed.eq(1),
                        If(source.stb & source.ack,
                                cmd_fifo.source.ack.eq(1),
@@ -243,11 +282,9 @@ class SATACommandRX(Module):
                ]
 
 class SATACommand(Module):
-       def __init__(self, transport, sector_size=512, max_count=4):
-               if max_count*sector_size > 8192:
-                       raise ValueError("sector_size * max_count must be <= 8192")
-               self.tx = SATACommandTX(transport, sector_size)
-               self.rx = SATACommandRX(transport, sector_size, max_count)
+       def __init__(self, transport):
+               self.tx = SATACommandTX(transport)
+               self.rx = SATACommandRX(transport)
                self.comb += [
                        self.rx.to_tx.connect(self.tx.from_rx),
                        self.tx.to_rx.connect(self.rx.from_tx)
index 98dd3001f337152e4697fbde92ffd30dc0f25efa..dd2723da05b21c4f098f592d7b2b5aff9e749f04 100644 (file)
@@ -1,3 +1,5 @@
+import math
+
 from migen.fhdl.std import *
 from migen.genlib.resetsync import *
 from migen.genlib.fsm import *
@@ -51,6 +53,8 @@ def link_description(dw):
        return EndpointDescription(layout, packetized=True)
 
 # Transport Layer
+fis_max_dwords = 2048
+
 fis_types = {
        "REG_H2D":          0x27,
        "REG_D2H":          0x34,
@@ -152,7 +156,7 @@ def command_tx_description(dw):
                ("write", 1),
                ("read", 1),
                ("sector", 48),
-               ("count", 4),
+               ("count", 16),
                ("data", dw)
        ]
        return EndpointDescription(layout, packetized=True)
@@ -161,6 +165,7 @@ def command_rx_description(dw):
        layout = [
                ("write", 1),
                ("read", 1),
+               ("last", 1),
                ("success", 1),
                ("failed", 1),
                ("data", dw)
@@ -171,8 +176,9 @@ def command_rx_cmd_description(dw):
        layout = [
                ("write", 1),
                ("read", 1),
+               ("last", 1),
                ("success", 1),
-               ("failed", 1)
+               ("failed", 1),
        ]
        return EndpointDescription(layout, packetized=False)
 
@@ -182,6 +188,16 @@ def command_rx_data_description(dw):
        ]
        return EndpointDescription(layout, packetized=True)
 
+# HDD
+logical_sector_size = 512 # constant since all HDDs use this
+
+def dwords2sectors(n):
+       return math.ceil(n*4/logical_sector_size)
+
+def sectors2dwords(n):
+       return n*logical_sector_size//4
+
+# Generic modules
 @DecorateModule(InsertReset)
 @DecorateModule(InsertCE)
 class Counter(Module):
index bc83a94468590dcb3f368d8fa33c8a32ea1ec5ef..a3a8114a4fd1fddd8b82a2c37f84cc21615c6dd6 100644 (file)
@@ -18,7 +18,7 @@ class TB(Module):
                hdd = self.hdd
                hdd.malloc(0, 64)
                selfp.bist.sector = 0
-               selfp.bist.count = 4
+               selfp.bist.count = 17
                while True:
                        selfp.bist.start = 1
                        yield
index 3915a2b7788dedb6c19c84bab1f6a96f834a2c39..4470dd8662f922a4883903810e820d0c6892dec0 100644 (file)
@@ -82,8 +82,8 @@ class TB(Module):
        def gen_simulation(self, selfp):
                hdd = self.hdd
                hdd.malloc(0, 64)
-               write_data = [i for i in range(hdd.sectors2dwords(2))]
-               write_len = hdd.dwords2sectors(len(write_data))
+               write_data = [i for i in range(sectors2dwords(2))]
+               write_len = dwords2sectors(len(write_data))
                write_packet = CommandTXPacket(write=1, sector=2, count=write_len, data=write_data)
                yield from self.streamer.send(write_packet)
                yield from self.logger.receive()
index 8783d06915d295630baaf1626f3a7eb2964248bf..e390d81bef646e4e91dc1f3b9344889932e8c042 100644 (file)
@@ -275,13 +275,6 @@ class LinkLayer(Module):
 def print_transport(s):
        print_with_prefix(s, "[TRN]: ")
 
-def _big2little(v):
-       return int.from_bytes(v.to_bytes(4, byteorder='big'), "little")
-
-def _little2big(v):
-       r = int.from_bytes(v.to_bytes(4, byteorder='little'), "big")
-       return r
-
 def get_field_data(field, packet):
        return (packet[field.dword] >> field.offset) & (2**field.width-1)
 
@@ -443,7 +436,7 @@ class HDD(Module):
        def __init__(self,
                        link_debug=False, link_random_level=0,
                        transport_debug=False, transport_loopback=False,
-                       hdd_debug=False, hdd_sector_size=512,
+                       hdd_debug=False,
                        ):
                ###
                self.phy = PHYLayer()
@@ -454,26 +447,21 @@ class HDD(Module):
                self.command.set_hdd(self)
 
                self.debug = hdd_debug
-               self.sector_size = hdd_sector_size
                self.mem = None
                self.wr_sector = 0
                self.wr_end_sector = 0
-
-       def dwords2sectors(self, n):
-               return math.ceil(n*4/self.sector_size)
-
-       def sectors2dwords(self, n):
-               return n*self.sector_size//4
+               self.rd_sector = 0
+               self.rx_end_sector = 0
 
        def malloc(self, sector, count):
                if self.debug:
                        s = "Allocating {n} sectors: {s} to {e}".format(n=count, s=sector, e=sector+count)
-                       s += " ({} KB)".format(count*self.sector_size//1024)
+                       s += " ({} KB)".format(count*logical_sector_size//1024)
                        print_hdd(s)
-               self.mem = HDDMemRegion(sector, count, self.sector_size)
+               self.mem = HDDMemRegion(sector, count, logical_sector_size)
 
        def write(self, sector, data):
-               n = math.ceil(self.dwords2sectors(len(data)))
+               n = math.ceil(dwords2sectors(len(data)))
                if self.debug:
                        if n == 1:
                                s = "{}".format(sector)
@@ -481,7 +469,7 @@ class HDD(Module):
                                s = "{s} to {e}".format(s=sector, e=sector+n-1)
                        print_hdd("Writing sector " + s)
                for i in range(len(data)):
-                       offset = self.sectors2dwords(sector)
+                       offset = sectors2dwords(sector)
                        self.mem.data[offset+i] = data[i]
 
        def read(self, sector, count):
@@ -492,8 +480,8 @@ class HDD(Module):
                                s = "{s} to {e}".format(s=sector, e=sector+count-1)
                        print_hdd("Reading sector " + s)
                data = []
-               for i in range(self.sectors2dwords(count)):
-                       data.append(self.mem.data[self.sectors2dwords(sector)+i])
+               for i in range(sectors2dwords(count)):
+                       data.append(self.mem.data[sectors2dwords(sector)+i])
                return data
 
        def write_dma_callback(self, fis):
@@ -502,14 +490,21 @@ class HDD(Module):
                return [FIS_DMA_ACTIVATE_D2H()]
 
        def read_dma_callback(self, fis):
-               sector = fis.lba_lsb + (fis.lba_msb << 32)
-               packet = self.read(sector, fis.count)
-               packet.insert(0, 0)
-               return [FIS_DATA(packet, direction="D2H"), FIS_REG_D2H()]
+               self.rd_sector = fis.lba_lsb + (fis.lba_msb << 32)
+               self.rd_end_sector = self.rd_sector + fis.count
+               packets = []
+               while self.rd_sector != self.rd_end_sector:
+                       count = min(self.rd_end_sector-self.rd_sector, (fis_max_dwords*4)//logical_sector_size)
+                       packet = self.read(self.rd_sector, count)
+                       packet.insert(0, 0)
+                       packets.append(FIS_DATA(packet, direction="D2H"))
+                       self.rd_sector += count
+               packets.append(FIS_REG_D2H())
+               return packets
 
        def data_callback(self, fis):
                self.write(self.wr_sector, fis.packet[1:])
-               self.wr_sector += self.dwords2sectors(len(fis.packet[1:]))
+               self.wr_sector += dwords2sectors(len(fis.packet[1:]))
                if self.wr_sector == self.wr_end_sector:
                        return [FIS_REG_D2H()]
                else:
index d925b8d2c3b9eec4ad3e96388bd331a351303bb4..41482fd6cc1ea61aa335f8a9eb238fb7a8f246bc 100644 (file)
@@ -2,7 +2,7 @@ import time
 import argparse
 from config import *
 
-sector_size = 512
+logical_sector_size = 512
 
 class SATABISTDriver:
        def __init__(self, regs):
@@ -46,7 +46,7 @@ class SATABISTDriver:
                        speed_mult = 1
                else:
                        speed_mult = 2
-               print("%4.2f MB/sec errors=%d sector=%d" %(n*sector_size*speed_mult/(1024*1024), errors, sector))
+               print("%4.2f MB/sec errors=%d sector=%d" %(n*logical_sector_size*speed_mult/(1024*1024), errors, sector))
 
 
 def _get_args():