tx_to_rx = [
("write", 1),
- ("read", 1)
+ ("read", 1),
+ ("count", 16)
]
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)
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),
)
)
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",
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")
)
)
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"),
)
)
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")
)
)
)
)
)
)
+ # 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),
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),
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),
]
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)
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)
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()
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)
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):
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):
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: