From a450079866076d5140136c8f3b3d247824c7e97f Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 6 Jan 2015 16:48:19 +0100 Subject: [PATCH] command: add support for larger DMAs --- lib/sata/__init__.py | 8 +-- lib/sata/bist.py | 12 ++-- lib/sata/command/__init__.py | 109 +++++++++++++++++++++++------------ lib/sata/common.py | 20 ++++++- lib/sata/test/bist_tb.py | 2 +- lib/sata/test/command_tb.py | 4 +- lib/sata/test/hdd.py | 47 +++++++-------- test/bist.py | 4 +- 8 files changed, 127 insertions(+), 79 deletions(-) diff --git a/lib/sata/__init__.py b/lib/sata/__init__.py index de72c7ef..34c7a353 100644 --- a/lib/sata/__init__.py +++ b/lib/sata/__init__.py @@ -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 diff --git a/lib/sata/bist.py b/lib/sata/bist.py index 1ee2f001..73b14ac5 100644 --- a/lib/sata/bist.py +++ b/lib/sata/bist.py @@ -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") + ) ) ) ) diff --git a/lib/sata/command/__init__.py b/lib/sata/command/__init__.py index 42ba8931..ba2297c7 100644 --- a/lib/sata/command/__init__.py +++ b/lib/sata/command/__init__.py @@ -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) diff --git a/lib/sata/common.py b/lib/sata/common.py index 98dd3001..dd2723da 100644 --- a/lib/sata/common.py +++ b/lib/sata/common.py @@ -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): diff --git a/lib/sata/test/bist_tb.py b/lib/sata/test/bist_tb.py index bc83a944..a3a8114a 100644 --- a/lib/sata/test/bist_tb.py +++ b/lib/sata/test/bist_tb.py @@ -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 diff --git a/lib/sata/test/command_tb.py b/lib/sata/test/command_tb.py index 3915a2b7..4470dd86 100644 --- a/lib/sata/test/command_tb.py +++ b/lib/sata/test/command_tb.py @@ -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() diff --git a/lib/sata/test/hdd.py b/lib/sata/test/hdd.py index 8783d069..e390d81b 100644 --- a/lib/sata/test/hdd.py +++ b/lib/sata/test/hdd.py @@ -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: diff --git a/test/bist.py b/test/bist.py index d925b8d2..41482fd6 100644 --- a/test/bist.py +++ b/test/bist.py @@ -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(): -- 2.30.2